mirror of
https://github.com/onyx-and-iris/xair-cli.git
synced 2026-04-18 06:43:33 +00:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c67887ad7 | |||
| 4db173154b | |||
| 27922d41bb | |||
| e21fd59564 | |||
| 2d829e28e1 | |||
| 3f8861ded2 | |||
| 1ad214ba4a | |||
| 66da965edd | |||
| 079a0b240d | |||
| fe711f79f1 | |||
| c94ac62cb8 | |||
| 5933b25114 | |||
| fa704832d5 | |||
| 69925021af | |||
| e092ed3c4e | |||
| d87bc2678c | |||
| c3221f3df5 | |||
| dc733ba500 | |||
| 2a5e0e022f | |||
| 663a863386 | |||
| 70889c47bc | |||
| 2ad9f2fd6a | |||
| c20aa82e3b | |||
| 1c5a75ffe1 | |||
| b075515654 | |||
| 139e092937 | |||
| 8539685770 | |||
| 8fc5a55eff | |||
| 128f0c1df6 | |||
| 49cf3ff49e | |||
| 66ab937296 | |||
| 6f995397a1 | |||
| e6d6ac77f6 | |||
| b39dc86236 | |||
| 056ebc9116 | |||
| ce955deb38 | |||
| 6e3953e946 | |||
| 86ff40952b | |||
| 2590a43c18 | |||
| 0559899666 | |||
|
|
20fed03c48 | ||
| fc36e9bf5c | |||
| a9110f0986 | |||
| 72f43452a8 | |||
| 89ab8ee258 | |||
| c4a86adf14 |
157
README.md
157
README.md
@@ -6,54 +6,144 @@
|
|||||||
go install github.com/onyx-and-iris/xair-cli@latest
|
go install github.com/onyx-and-iris/xair-cli@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
#### Flags
|
||||||
|
|
||||||
|
- --host/-H: Host of the mixer.
|
||||||
|
- --port/-P: Port of the mixer.
|
||||||
|
- --kind/-k: The kind of mixer. May be one of (*xair*, *x32*).
|
||||||
|
- Use this flag to connect to an x32 mixer.
|
||||||
|
|
||||||
|
#### Environment Variables
|
||||||
|
|
||||||
|
Example .envrc:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
XAIR_CLI_HOST=mixer.local
|
||||||
|
XAIR_CLI_PORT=10024
|
||||||
|
XAIR_CLI_KIND=xair
|
||||||
|
XAIR_CLI_TIMEOUT=100ms
|
||||||
|
```
|
||||||
|
|
||||||
### Use
|
### Use
|
||||||
|
|
||||||
```console
|
```console
|
||||||
xair-cli is a command-line tool that allows users to send OSC messages
|
Usage: xair-cli <command> [flags]
|
||||||
to Behringer X Air mixers for remote control and configuration. It supports
|
|
||||||
various commands to manage mixer settings directly from the terminal.
|
|
||||||
|
|
||||||
Usage:
|
A CLI to control Behringer X-Air mixers.
|
||||||
xair-cli [flags]
|
|
||||||
xair-cli [command]
|
|
||||||
|
|
||||||
Available Commands:
|
|
||||||
bus Commands to control individual buses
|
|
||||||
completion Generate the autocompletion script for the specified shell
|
|
||||||
headamp Commands to control headamp gain and phantom power
|
|
||||||
help Help about any command
|
|
||||||
main Commands to control the main output
|
|
||||||
strip Commands to control individual strips
|
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for xair-cli
|
-h, --help Show context-sensitive help.
|
||||||
-H, --host string host address of the X Air mixer (default "mixer.local")
|
-H, --host="mixer.local" The host of the X-Air device ($XAIR_CLI_HOST).
|
||||||
-k, --kind string Kind of mixer (xair, x32) (default "xair")
|
-P, --port=10024 The port of the X-Air device ($XAIR_CLI_PORT).
|
||||||
-l, --loglevel string Log level (debug, info, warn, error, fatal, panic) (default "warn")
|
-K, --kind="xair" The kind of the X-Air device ($XAIR_CLI_KIND).
|
||||||
-p, --port int Port number of the X Air mixer (default 10024)
|
-T, --timeout=100ms Timeout for OSC operations ($XAIR_CLI_TIMEOUT).
|
||||||
-v, --version version for xair-cli
|
-L, --loglevel="warn" Log level for the CLI ($XAIR_CLI_LOGLEVEL).
|
||||||
|
-v, --version Print xair-cli version information and quit
|
||||||
|
|
||||||
Use "xair-cli [command] --help" for more information about a command.
|
Commands:
|
||||||
|
completion (c) Generate shell completion scripts.
|
||||||
|
|
||||||
|
Raw
|
||||||
|
raw Send raw OSC messages to the mixer.
|
||||||
|
|
||||||
|
Main
|
||||||
|
main mute Get or set the mute state of the Main L/R output.
|
||||||
|
main fader Get or set the fader level of the Main L/R output.
|
||||||
|
main fadein Fade in the Main L/R output over a specified duration.
|
||||||
|
main fadeout Fade out the Main L/R output over a specified duration.
|
||||||
|
|
||||||
|
Strip
|
||||||
|
strip <index> mute Get or set the mute state of the strip.
|
||||||
|
strip <index> fader Get or set the fader level of the strip.
|
||||||
|
strip <index> fadein Fade in the strip over a specified duration.
|
||||||
|
strip <index> fadeout Fade out the strip over a specified duration.
|
||||||
|
strip <index> send Get or set the send level for a specific bus.
|
||||||
|
strip <index> name Get or set the name of the strip.
|
||||||
|
strip <index> gate on Get or set the gate on/off state of the strip.
|
||||||
|
strip <index> gate mode Get or set the gate mode of the strip.
|
||||||
|
strip <index> gate threshold Get or set the gate threshold of the strip.
|
||||||
|
strip <index> gate range Get or set the gate range of the strip.
|
||||||
|
strip <index> gate attack Get or set the gate attack time of the strip.
|
||||||
|
strip <index> gate hold Get or set the gate hold time of the strip.
|
||||||
|
strip <index> gate release Get or set the gate release time of the strip.
|
||||||
|
strip <index> eq on Get or set the EQ on/off state of the strip.
|
||||||
|
strip <index> eq <band> gain Get or set the gain of the EQ band.
|
||||||
|
strip <index> eq <band> freq Get or set the frequency of the EQ band.
|
||||||
|
strip <index> eq <band> q Get or set the Q factor of the EQ band.
|
||||||
|
strip <index> eq <band> type Get or set the type of the EQ band.
|
||||||
|
strip <index> comp on Get or set the compressor on/off state of the strip.
|
||||||
|
strip <index> comp mode Get or set the compressor mode of the strip.
|
||||||
|
strip <index> comp threshold Get or set the compressor threshold of the strip.
|
||||||
|
strip <index> comp ratio Get or set the compressor ratio of the strip.
|
||||||
|
strip <index> comp mix Get or set the compressor mix of the strip.
|
||||||
|
strip <index> comp makeup Get or set the compressor makeup gain of the strip.
|
||||||
|
strip <index> comp attack Get or set the compressor attack time of the strip.
|
||||||
|
strip <index> comp hold Get or set the compressor hold time of the strip.
|
||||||
|
strip <index> comp release Get or set the compressor release time of the strip.
|
||||||
|
|
||||||
|
Bus
|
||||||
|
bus <index> mute Get or set the mute state of the bus.
|
||||||
|
bus <index> fader Get or set the fader level of the bus.
|
||||||
|
bus <index> fadein Fade in the bus over a specified duration.
|
||||||
|
bus <index> fadeout Fade out the bus over a specified duration.
|
||||||
|
bus <index> name Get or set the name of the bus.
|
||||||
|
bus <index> eq on Get or set the EQ on/off state of the bus.
|
||||||
|
bus <index> eq mode Get or set the EQ mode of the bus (peq, geq or teq).
|
||||||
|
bus <index> eq <band> gain Get or set the gain of the EQ band.
|
||||||
|
bus <index> eq <band> freq Get or set the frequency of the EQ band.
|
||||||
|
bus <index> eq <band> q Get or set the Q factor of the EQ band.
|
||||||
|
bus <index> eq <band> type Get or set the type of the EQ band (lcut, lshv, peq, veq, hshv, hcut).
|
||||||
|
bus <index> comp on Get or set the compressor on/off state of the bus.
|
||||||
|
bus <index> comp mode Get or set the compressor mode of the bus (comp, exp).
|
||||||
|
bus <index> comp threshold Get or set the compressor threshold of the bus (in dB).
|
||||||
|
bus <index> comp ratio Get or set the compressor ratio of the bus.
|
||||||
|
bus <index> comp mix Get or set the compressor mix level of the bus (in %).
|
||||||
|
bus <index> comp makeup Get or set the compressor makeup gain of the bus (in dB).
|
||||||
|
bus <index> comp attack Get or set the compressor attack time of the bus (in ms).
|
||||||
|
bus <index> comp hold Get or set the compressor hold time of the bus (in ms).
|
||||||
|
bus <index> comp release Get or set the compressor release time of the bus (in ms).
|
||||||
|
|
||||||
|
Headamp
|
||||||
|
headamp <index> gain Get or set the gain of the headamp.
|
||||||
|
headamp <index> phantom Get or set the phantom power state of the headamp.
|
||||||
|
|
||||||
|
Snapshot
|
||||||
|
snapshot list List all snapshots.
|
||||||
|
snapshot <index> name Get or set the name of a snapshot.
|
||||||
|
snapshot <index> save Save the current mixer state.
|
||||||
|
snapshot <index> load Load a mixer state.
|
||||||
|
snapshot <index> delete Delete a snapshot.
|
||||||
|
|
||||||
|
Run "xair-cli <command> --help" for more information on a command.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
*Fade out main LR all the way to -∞*
|
*Fade out main LR all the way to -∞ over a 5s duration*
|
||||||
|
|
||||||
```console
|
```console
|
||||||
xair-cli main fadeout
|
xair-cli main fadeout
|
||||||
```
|
```
|
||||||
|
|
||||||
*enable phantom power and set the gain to 28.0dB over a 10s duration for strip 09*
|
*enable phantom power and set the gain to 28.0dB over a 10s duration for headamp (strip) 09*
|
||||||
```console
|
```console
|
||||||
xair-cli headamp 9 phantom on
|
xair-cli headamp 9 phantom on
|
||||||
|
|
||||||
xair-cli headamp 9 gain 28.0 --duration 10s
|
xair-cli headamp 9 gain --duration 10s 18.0
|
||||||
```
|
```
|
||||||
|
|
||||||
*set strip 09 send level for bus 5 to -18.0dB*
|
*set strip 09 send level for bus 5 to -18.0dB*
|
||||||
```console
|
```console
|
||||||
xair-cli strip send 9 5 -- -18.0
|
xair-cli strip 9 send 5 -- -18.0
|
||||||
|
```
|
||||||
|
|
||||||
|
*enable eq for strip 01*
|
||||||
|
```console
|
||||||
|
xair-cli strip 1 eq on true
|
||||||
```
|
```
|
||||||
|
|
||||||
*rename bus 01 to 'vocal mix'*
|
*rename bus 01 to 'vocal mix'*
|
||||||
@@ -61,12 +151,25 @@ xair-cli strip send 9 5 -- -18.0
|
|||||||
xair-cli bus 1 name 'vocal mix'
|
xair-cli bus 1 name 'vocal mix'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*set bus 03 eq band 03 (LoMid) gain*
|
||||||
|
```console
|
||||||
|
xair-cli bus 3 eq 3 gain -- -3.5
|
||||||
|
```
|
||||||
|
|
||||||
### Notes
|
*Send a raw OSC message to the mixer*
|
||||||
|
```console
|
||||||
|
xair-cli raw /xinfo
|
||||||
|
|
||||||
I've only implemented the parts I personally need, I don't know how much more I intend to add.
|
xair-cli raw /ch/01/config/name 'rode podmic'
|
||||||
|
xair-cli raw /ch/01/config/name
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
`xair-cli` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
`xair-cli` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
||||||
|
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
For an alternative, python implementation consider checking out [dc-xair-cli](https://pypi.org/project/dc-xair-cli/). It supports some operations like batch commands and network discovery which this CLI doesn't (and I have no plans to implement them).
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ vars:
|
|||||||
|
|
||||||
WINDOWS: '{{.BIN_DIR}}/{{.PROGRAM}}_windows_amd64.exe'
|
WINDOWS: '{{.BIN_DIR}}/{{.PROGRAM}}_windows_amd64.exe'
|
||||||
LINUX: '{{.BIN_DIR}}/{{.PROGRAM}}_linux_amd64'
|
LINUX: '{{.BIN_DIR}}/{{.PROGRAM}}_linux_amd64'
|
||||||
|
MACOS: '{{.BIN_DIR}}/{{.PROGRAM}}_darwin_amd64'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
default:
|
default:
|
||||||
@@ -22,6 +23,7 @@ tasks:
|
|||||||
cmds:
|
cmds:
|
||||||
- task: build-windows
|
- task: build-windows
|
||||||
- task: build-linux
|
- task: build-linux
|
||||||
|
- task: build-macos
|
||||||
|
|
||||||
vet:
|
vet:
|
||||||
desc: Vet the code
|
desc: Vet the code
|
||||||
@@ -46,6 +48,12 @@ tasks:
|
|||||||
- GOOS=linux GOARCH=amd64 go build -o {{.LINUX}} -ldflags="-X main.version={{.VERSION}}"
|
- GOOS=linux GOARCH=amd64 go build -o {{.LINUX}} -ldflags="-X main.version={{.VERSION}}"
|
||||||
internal: true
|
internal: true
|
||||||
|
|
||||||
|
build-macos:
|
||||||
|
desc: Build the xair-cli project for macOS
|
||||||
|
cmds:
|
||||||
|
- GOOS=darwin GOARCH=amd64 go build -o {{.MACOS}} -ldflags="-X main.version={{.VERSION}}"
|
||||||
|
internal: true
|
||||||
|
|
||||||
test:
|
test:
|
||||||
desc: Run tests
|
desc: Run tests
|
||||||
cmds:
|
cmds:
|
||||||
|
|||||||
549
bus.go
Normal file
549
bus.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
269
cmd/bus.go
269
cmd/bus.go
@@ -1,269 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// busCmd represents the bus command.
|
|
||||||
var busCmd = &cobra.Command{
|
|
||||||
Short: "Commands to control individual buses",
|
|
||||||
Long: `Commands to control individual buses of the XAir mixer, including mute status.`,
|
|
||||||
Use: "bus",
|
|
||||||
Run: func(cmd *cobra.Command, _ []string) {
|
|
||||||
cmd.Help()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// busMuteCmd represents the bus mute command.
|
|
||||||
var busMuteCmd = &cobra.Command{
|
|
||||||
Short: "Get or set the bus mute status",
|
|
||||||
Long: `Get or set the mute status of a specific bus.`,
|
|
||||||
Use: "mute [bus number] [true|false]",
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 2 {
|
|
||||||
return fmt.Errorf("Please provide bus number and mute status (true/false)")
|
|
||||||
}
|
|
||||||
|
|
||||||
busNum := mustConvToInt(args[0])
|
|
||||||
var muted bool
|
|
||||||
switch args[1] {
|
|
||||||
case "true", "1":
|
|
||||||
muted = true
|
|
||||||
case "false", "0":
|
|
||||||
muted = false
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Invalid mute status. Use true/false or 1/0")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := client.Bus.SetMute(busNum, muted)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting bus mute status: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Bus %d mute set to %v\n", busNum, muted)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// busFaderCmd represents the bus fader command.
|
|
||||||
var busFaderCmd = &cobra.Command{
|
|
||||||
Short: "Get or set the bus fader level",
|
|
||||||
Long: `Get or set the fader level of a specific bus.
|
|
||||||
If no level argument is provided, the current fader level is retrieved.
|
|
||||||
If a level argument (in dB) is provided, the bus fader is set to that level.`,
|
|
||||||
Use: "fader [bus number] [level in dB]",
|
|
||||||
Example: ` # Get the current fader level of bus 1
|
|
||||||
xair-cli bus fader 1
|
|
||||||
|
|
||||||
# Set the fader level of bus 1 to -10.0 dB
|
|
||||||
xair-cli bus fader 1 -10.0`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
busIndex := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
if len(args) == 1 {
|
|
||||||
level, err := client.Bus.Fader(busIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting bus fader level: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Bus %d fader level: %.1f dB\n", busIndex, level)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 2 {
|
|
||||||
return fmt.Errorf("Please provide bus number and fader level (in dB)")
|
|
||||||
}
|
|
||||||
|
|
||||||
level := mustConvToFloat64(args[1])
|
|
||||||
|
|
||||||
err := client.Bus.SetFader(busIndex, level)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting bus fader level: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Bus %d fader set to %.2f dB\n", busIndex, level)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// busFadeOutCmd represents the bus fade out command.
|
|
||||||
var busFadeOutCmd = &cobra.Command{
|
|
||||||
Short: "Fade out the bus fader over a specified duration",
|
|
||||||
Long: "Fade out the bus fader to minimum level over a specified duration in seconds.",
|
|
||||||
Use: "fadeout [bus number] --duration [seconds] [target level in dB]",
|
|
||||||
Example: ` # Fade out bus 1 over 5 seconds
|
|
||||||
xair-cli bus fadeout 1 --duration 5s -- -90.0`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide bus number")
|
|
||||||
}
|
|
||||||
|
|
||||||
busIndex := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
duration, err := cmd.Flags().GetDuration("duration")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting duration flag: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
target := -90.0
|
|
||||||
if len(args) > 1 {
|
|
||||||
target = mustConvToFloat64(args[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
currentFader, err := client.Bus.Fader(busIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting current bus fader level: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate total steps needed to reach target dB
|
|
||||||
totalSteps := float64(currentFader - target)
|
|
||||||
if totalSteps <= 0 {
|
|
||||||
cmd.Println("Bus is already at or below target level")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
stepDelay := time.Duration(duration.Seconds()*1000/totalSteps) * time.Millisecond
|
|
||||||
|
|
||||||
for currentFader > target {
|
|
||||||
currentFader -= 1.0
|
|
||||||
err := client.Bus.SetFader(busIndex, currentFader)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting bus fader level: %w", err)
|
|
||||||
}
|
|
||||||
time.Sleep(stepDelay)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Println("Bus fade out completed")
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// BusFadeInCmd represents the bus fade in command.
|
|
||||||
var busFadeInCmd = &cobra.Command{
|
|
||||||
Short: "Fade in the bus fader over a specified duration",
|
|
||||||
Long: "Fade in the bus fader to maximum level over a specified duration in seconds.",
|
|
||||||
Use: "fadein [bus number] --duration [seconds] [target level in dB]",
|
|
||||||
Example: ` # Fade in bus 1 over 5 seconds
|
|
||||||
xair-cli bus fadein 1 --duration 5s -- 0.0`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide bus number")
|
|
||||||
}
|
|
||||||
|
|
||||||
busIndex := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
duration, err := cmd.Flags().GetDuration("duration")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting duration flag: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
target := 0.0
|
|
||||||
if len(args) > 1 {
|
|
||||||
target = mustConvToFloat64(args[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
currentFader, err := client.Bus.Fader(busIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting current bus fader level: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate total steps needed to reach target dB
|
|
||||||
totalSteps := float64(target - currentFader)
|
|
||||||
if totalSteps <= 0 {
|
|
||||||
cmd.Println("Bus is already at or above target level")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
stepDelay := time.Duration(duration.Seconds()*1000/totalSteps) * time.Millisecond
|
|
||||||
|
|
||||||
for currentFader < target {
|
|
||||||
currentFader += 1.0
|
|
||||||
err := client.Bus.SetFader(busIndex, currentFader)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting bus fader level: %w", err)
|
|
||||||
}
|
|
||||||
time.Sleep(stepDelay)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Println("Bus fade in completed")
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// busNameCmd represents the bus name command.
|
|
||||||
var busNameCmd = &cobra.Command{
|
|
||||||
Short: "Get or set the bus name",
|
|
||||||
Long: `Get or set the name of a specific bus.`,
|
|
||||||
Use: "name [bus number] [new name]",
|
|
||||||
Example: ` # Get the name of bus 1
|
|
||||||
xair-cli bus name 1
|
|
||||||
|
|
||||||
# Set the name of bus 1 to "Vocals"
|
|
||||||
xair-cli bus name 1 Vocals`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide bus number")
|
|
||||||
}
|
|
||||||
|
|
||||||
busIndex := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
if len(args) == 1 {
|
|
||||||
name, err := client.Bus.Name(busIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting bus name: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Bus %d name: %s\n", busIndex, name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
newName := args[1]
|
|
||||||
err := client.Bus.SetName(busIndex, newName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting bus name: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Bus %d name set to: %s\n", busIndex, newName)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(busCmd)
|
|
||||||
|
|
||||||
busCmd.AddCommand(busMuteCmd)
|
|
||||||
|
|
||||||
busCmd.AddCommand(busFaderCmd)
|
|
||||||
busCmd.AddCommand(busFadeOutCmd)
|
|
||||||
busFadeOutCmd.Flags().DurationP("duration", "d", 5*time.Second, "Duration for fade out in seconds")
|
|
||||||
busCmd.AddCommand(busFadeInCmd)
|
|
||||||
busFadeInCmd.Flags().DurationP("duration", "d", 5*time.Second, "Duration for fade in in seconds")
|
|
||||||
|
|
||||||
busCmd.AddCommand(busNameCmd)
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/onyx-and-iris/xair-cli/internal/xair"
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientKey string
|
|
||||||
|
|
||||||
// WithContext returns a new context with the provided xair.Client.
|
|
||||||
func WithContext(ctx context.Context, client *xair.Client) context.Context {
|
|
||||||
return context.WithValue(ctx, clientKey("oscClient"), client)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientFromContext retrieves the xair.Client from the context.
|
|
||||||
func ClientFromContext(ctx context.Context) *xair.Client {
|
|
||||||
if client, ok := ctx.Value(clientKey("oscClient")).(*xair.Client); ok {
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
211
cmd/headamp.go
211
cmd/headamp.go
@@ -1,211 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
|
||||||
"github.com/onyx-and-iris/xair-cli/internal/xair"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// headampCmd represents the headamp command
|
|
||||||
var headampCmd = &cobra.Command{
|
|
||||||
Short: "Commands to control headamp gain and phantom power",
|
|
||||||
Long: `Commands to control the headamp gain and phantom power settings of the XAir mixer.
|
|
||||||
|
|
||||||
You can get or set the gain level for individual headamps, as well as enable or disable phantom power.`,
|
|
||||||
Use: "headamp",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
cmd.Help()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// headampGainCmd represents the headamp gain command
|
|
||||||
var headampGainCmd = &cobra.Command{
|
|
||||||
Use: "gain",
|
|
||||||
Short: "Get or set headamp gain level",
|
|
||||||
Long: `Get or set the gain level for a specified headamp index.
|
|
||||||
When setting gain, it will gradually increase from the current level to prevent
|
|
||||||
sudden jumps that could cause feedback or equipment damage.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
# Get gain level for headamp index 1
|
|
||||||
xair-cli headamp gain 1
|
|
||||||
# Set gain level for headamp index 1 to 3.5 dB (gradually over 5 seconds)
|
|
||||||
xair-cli headamp gain 1 3.5
|
|
||||||
# Set gain level for headamp index 1 to 3.5 dB over 10 seconds
|
|
||||||
xair-cli headamp gain 1 3.5 --duration 10s`,
|
|
||||||
Args: cobra.RangeArgs(1, 2),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide a headamp index")
|
|
||||||
}
|
|
||||||
|
|
||||||
index := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
if len(args) == 1 {
|
|
||||||
gain, err := client.HeadAmp.Gain(index)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting headamp gain level: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Headamp %d Gain: %.2f dB\n", index, gain)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 2 {
|
|
||||||
return fmt.Errorf("Please provide a gain level in dB")
|
|
||||||
}
|
|
||||||
|
|
||||||
targetLevel := mustConvToFloat64(args[1])
|
|
||||||
|
|
||||||
currentGain, err := client.HeadAmp.Gain(index)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting current headamp gain level: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
duration, err := cmd.Flags().GetDuration("duration")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting duration flag: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentGain == targetLevel {
|
|
||||||
cmd.Printf("Headamp %d Gain already at %.2f dB\n", index, targetLevel)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gradualGainAdjust(client, cmd, index, currentGain, targetLevel, duration); err != nil {
|
|
||||||
return fmt.Errorf("Error adjusting headamp gain level: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Headamp %d Gain set to %.2f dB\n", index, targetLevel)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// gradualGainAdjust gradually adjusts gain from current to target over specified duration
|
|
||||||
func gradualGainAdjust(
|
|
||||||
client *xair.Client,
|
|
||||||
cmd *cobra.Command,
|
|
||||||
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 := 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// headampPhantomPowerCmd represents the headamp phantom power command
|
|
||||||
var headampPhantomPowerCmd = &cobra.Command{
|
|
||||||
Use: "phantom",
|
|
||||||
Short: "Get or set headamp phantom power status",
|
|
||||||
Long: `Get or set the phantom power status for a specified headamp index.
|
|
||||||
Examples:
|
|
||||||
# Get phantom power status for headamp index 1
|
|
||||||
xairctl headamp phantom 1
|
|
||||||
# Enable phantom power for headamp index 1
|
|
||||||
xairctl headamp phantom 1 on
|
|
||||||
# Disable phantom power for headamp index 1
|
|
||||||
xairctl headamp phantom 1 off`,
|
|
||||||
Args: cobra.RangeArgs(1, 2),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide a headamp index")
|
|
||||||
}
|
|
||||||
|
|
||||||
index := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
if len(args) == 1 {
|
|
||||||
enabled, err := client.HeadAmp.PhantomPower(index)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting headamp phantom power status: %w", err)
|
|
||||||
}
|
|
||||||
status := "disabled"
|
|
||||||
if enabled {
|
|
||||||
status = "enabled"
|
|
||||||
}
|
|
||||||
cmd.Printf("Headamp %d Phantom Power is %s\n", index, status)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 2 {
|
|
||||||
return fmt.Errorf("Please provide phantom power status: on or off")
|
|
||||||
}
|
|
||||||
|
|
||||||
var enable bool
|
|
||||||
switch args[1] {
|
|
||||||
case "on", "enable":
|
|
||||||
enable = true
|
|
||||||
case "off", "disable":
|
|
||||||
enable = false
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Invalid phantom power status. Use 'on' or 'off'")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := client.HeadAmp.SetPhantomPower(index, enable)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting headamp phantom power status: %w", err)
|
|
||||||
}
|
|
||||||
status := "disabled"
|
|
||||||
if enable {
|
|
||||||
status = "enabled"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Headamp %d Phantom Power %s successfully\n", index, status)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(headampCmd)
|
|
||||||
|
|
||||||
headampCmd.AddCommand(headampGainCmd)
|
|
||||||
headampGainCmd.Flags().DurationP("duration", "d", 5*time.Second, "Duration over which to gradually adjust gain")
|
|
||||||
|
|
||||||
headampCmd.AddCommand(headampPhantomPowerCmd)
|
|
||||||
}
|
|
||||||
237
cmd/main.go
237
cmd/main.go
@@ -1,237 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// mainCmd represents the main command.
|
|
||||||
var mainCmd = &cobra.Command{
|
|
||||||
Short: "Commands to control the main output",
|
|
||||||
Long: `Commands to control the main output of the XAir mixer, including fader level and mute status.`,
|
|
||||||
Use: "main",
|
|
||||||
Run: func(cmd *cobra.Command, _ []string) {
|
|
||||||
cmd.Help()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// mainMuteCmd represents the main mute command.
|
|
||||||
var mainMuteCmd = &cobra.Command{
|
|
||||||
Short: "Get or set the main LR mute status",
|
|
||||||
Long: `Get or set the main L/R mute status.
|
|
||||||
|
|
||||||
If no argument is provided, the current mute status is retrieved.
|
|
||||||
If "true" or "1" is provided as an argument, the main output is muted.
|
|
||||||
If "false" or "0" is provided, the main output is unmuted.`,
|
|
||||||
Use: "mute [true|false]",
|
|
||||||
Example: ` # Get the current main LR mute status
|
|
||||||
xair-cli main mute
|
|
||||||
|
|
||||||
# Mute the main output
|
|
||||||
xair-cli main mute true
|
|
||||||
|
|
||||||
# Unmute the main output
|
|
||||||
xair-cli main mute false`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) == 0 {
|
|
||||||
resp, err := client.Main.Mute()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting main LR mute status: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Main LR mute: %v\n", resp)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var muted bool
|
|
||||||
switch args[0] {
|
|
||||||
case "true", "1":
|
|
||||||
muted = true
|
|
||||||
case "false", "0":
|
|
||||||
muted = false
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Invalid mute status. Use true/false or 1/0")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := client.Main.SetMute(muted)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting main LR mute status: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Println("Main LR mute status set successfully")
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// mainFaderCmd represents the main fader command.
|
|
||||||
var mainFaderCmd = &cobra.Command{
|
|
||||||
Short: "Set or get the main LR fader level",
|
|
||||||
Long: `Set or get the main L/R fader level in dB.
|
|
||||||
|
|
||||||
If no argument is provided, the current fader level is retrieved.
|
|
||||||
If a dB value is provided as an argument, the fader level is set to that value.`,
|
|
||||||
Use: "fader [level in dB]",
|
|
||||||
Example: ` # Get the current main LR fader level
|
|
||||||
xair-cli main fader
|
|
||||||
|
|
||||||
# Set the main LR fader level to -10.0 dB
|
|
||||||
xair-cli main fader -- -10.0`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) == 0 {
|
|
||||||
resp, err := client.Main.Fader()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting main LR fader: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Main LR fader: %.1f dB\n", resp)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := client.Main.SetFader(mustConvToFloat64(args[0]))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting main LR fader: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Println("Main LR fader set successfully")
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// mainFadeOutCmd represents the main fadeout command.
|
|
||||||
var mainFadeOutCmd = &cobra.Command{
|
|
||||||
Short: "Fade out the main output",
|
|
||||||
Long: `Fade out the main output over a specified duration.
|
|
||||||
|
|
||||||
This command will fade out the main output to the specified dB level.
|
|
||||||
`,
|
|
||||||
Use: "fadeout --duration [seconds] [target_db]",
|
|
||||||
Example: ` # Fade out main output over 5 seconds
|
|
||||||
xair-cli main fadeout --duration 5s -- -90.0`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
cmd.PrintErrln("OSC client not found in context")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
duration, err := cmd.Flags().GetDuration("duration")
|
|
||||||
if err != nil {
|
|
||||||
cmd.PrintErrln("Error getting duration flag:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default target for fadeout
|
|
||||||
target := -90.0
|
|
||||||
if len(args) > 0 {
|
|
||||||
target = mustConvToFloat64(args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
currentFader, err := client.Main.Fader()
|
|
||||||
if err != nil {
|
|
||||||
cmd.PrintErrln("Error getting current main LR fader:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate total steps needed to reach target dB
|
|
||||||
totalSteps := float64(currentFader - target)
|
|
||||||
if totalSteps <= 0 {
|
|
||||||
cmd.Println("Main output is already faded out")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate delay per step to achieve exact duration
|
|
||||||
stepDelay := time.Duration(duration.Seconds()*1000/totalSteps) * time.Millisecond
|
|
||||||
|
|
||||||
for currentFader > target {
|
|
||||||
currentFader -= 1.0
|
|
||||||
err = client.Main.SetFader(currentFader)
|
|
||||||
if err != nil {
|
|
||||||
cmd.PrintErrln("Error setting main LR fader:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
time.Sleep(stepDelay)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Println("Main output faded out successfully")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// mainFadeInCmd represents the main fadein command.
|
|
||||||
var mainFadeInCmd = &cobra.Command{
|
|
||||||
Short: "Fade in the main output",
|
|
||||||
Long: `Fade in the main output over a specified duration.
|
|
||||||
|
|
||||||
This command will fade in the main output to the specified dB level.
|
|
||||||
`,
|
|
||||||
Use: "fadein --duration [seconds] [target_db]",
|
|
||||||
Example: ` # Fade in main output over 5 seconds
|
|
||||||
xair-cli main fadein --duration 5s -- 0.0`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
cmd.PrintErrln("OSC client not found in context")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
duration, err := cmd.Flags().GetDuration("duration")
|
|
||||||
if err != nil {
|
|
||||||
cmd.PrintErrln("Error getting duration flag:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
target := 0.0
|
|
||||||
if len(args) > 0 {
|
|
||||||
target = mustConvToFloat64(args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
currentFader, err := client.Main.Fader()
|
|
||||||
if err != nil {
|
|
||||||
cmd.PrintErrln("Error getting current main LR fader:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate total steps needed to reach target dB
|
|
||||||
totalSteps := float64(target - currentFader)
|
|
||||||
if totalSteps <= 0 {
|
|
||||||
cmd.Println("Main output is already at or above target level")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate delay per step to achieve exact duration
|
|
||||||
stepDelay := time.Duration(duration.Seconds()*1000/totalSteps) * time.Millisecond
|
|
||||||
|
|
||||||
for currentFader < target {
|
|
||||||
currentFader += 1.0
|
|
||||||
err = client.Main.SetFader(currentFader)
|
|
||||||
if err != nil {
|
|
||||||
cmd.PrintErrln("Error setting main LR fader:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
time.Sleep(stepDelay)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Println("Main output faded in successfully")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(mainCmd)
|
|
||||||
|
|
||||||
mainCmd.AddCommand(mainMuteCmd)
|
|
||||||
|
|
||||||
mainCmd.AddCommand(mainFaderCmd)
|
|
||||||
mainCmd.AddCommand(mainFadeOutCmd)
|
|
||||||
mainFadeOutCmd.Flags().DurationP("duration", "d", 5*time.Second, "Duration for fade out in seconds")
|
|
||||||
mainCmd.AddCommand(mainFadeInCmd)
|
|
||||||
mainFadeInCmd.Flags().DurationP("duration", "d", 5*time.Second, "Duration for fade in in seconds")
|
|
||||||
}
|
|
||||||
106
cmd/root.go
106
cmd/root.go
@@ -1,106 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/onyx-and-iris/xair-cli/internal/xair"
|
|
||||||
)
|
|
||||||
|
|
||||||
var version string // Version of the CLI, set during build time
|
|
||||||
|
|
||||||
// rootCmd represents the base command when called without any subcommands.
|
|
||||||
var rootCmd = &cobra.Command{
|
|
||||||
Use: "xair-cli",
|
|
||||||
Short: "A command-line utility to interact with Behringer X Air mixers via OSC",
|
|
||||||
Long: `xair-cli is a command-line tool that allows users to send OSC messages
|
|
||||||
to Behringer X Air mixers for remote control and configuration. It supports
|
|
||||||
various commands to manage mixer settings directly from the terminal.`,
|
|
||||||
Version: versionFromBuild(),
|
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
|
||||||
level, err := log.ParseLevel(viper.GetString("loglevel"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.SetLevel(level)
|
|
||||||
|
|
||||||
kind := viper.GetString("kind")
|
|
||||||
log.Debugf("Initialising client for mixer kind: %s", kind)
|
|
||||||
|
|
||||||
if kind == "x32" && !viper.IsSet("port") {
|
|
||||||
viper.Set("port", 10023)
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := xair.NewClient(
|
|
||||||
viper.GetString("host"),
|
|
||||||
viper.GetInt("port"),
|
|
||||||
xair.WithKind(kind),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cmd.SetContext(WithContext(cmd.Context(), client))
|
|
||||||
|
|
||||||
client.StartListening()
|
|
||||||
err, resp := client.RequestInfo()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("Received mixer info: %+v", resp)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
PersistentPostRunE: func(cmd *cobra.Command, _ []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client != nil {
|
|
||||||
client.Stop()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Run: func(cmd *cobra.Command, _ []string) {
|
|
||||||
cmd.Help()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
|
||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
|
||||||
func Execute() {
|
|
||||||
err := rootCmd.Execute()
|
|
||||||
if err != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.PersistentFlags().StringP("host", "H", "mixer.local", "host address of the X Air mixer")
|
|
||||||
rootCmd.PersistentFlags().IntP("port", "p", 10024, "Port number of the X Air mixer")
|
|
||||||
rootCmd.PersistentFlags().
|
|
||||||
StringP("loglevel", "l", "warn", "Log level (debug, info, warn, error, fatal, panic)")
|
|
||||||
rootCmd.PersistentFlags().StringP("kind", "k", "xair", "Kind of mixer (xair, x32)")
|
|
||||||
|
|
||||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
|
||||||
viper.SetEnvPrefix("XAIR_CLI")
|
|
||||||
viper.AutomaticEnv()
|
|
||||||
viper.BindPFlag("host", rootCmd.PersistentFlags().Lookup("host"))
|
|
||||||
viper.BindPFlag("port", rootCmd.PersistentFlags().Lookup("port"))
|
|
||||||
viper.BindPFlag("loglevel", rootCmd.PersistentFlags().Lookup("loglevel"))
|
|
||||||
viper.BindPFlag("kind", rootCmd.PersistentFlags().Lookup("kind"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func versionFromBuild() string {
|
|
||||||
if version == "" {
|
|
||||||
info, ok := debug.ReadBuildInfo()
|
|
||||||
if !ok {
|
|
||||||
return "(unable to read version)"
|
|
||||||
}
|
|
||||||
version = strings.Split(info.Main.Version, "-")[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return version
|
|
||||||
}
|
|
||||||
350
cmd/strip.go
350
cmd/strip.go
@@ -1,350 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// stripCmd represents the strip command.
|
|
||||||
var stripCmd = &cobra.Command{
|
|
||||||
Short: "Commands to control individual strips",
|
|
||||||
Long: `Commands to control individual strips of the XAir mixer, including fader level and mute status.`,
|
|
||||||
Use: "strip",
|
|
||||||
Run: func(cmd *cobra.Command, _ []string) {
|
|
||||||
cmd.Help()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// stripMuteCmd represents the strip mute command.
|
|
||||||
var stripMuteCmd = &cobra.Command{
|
|
||||||
Short: "Get or set the mute status of a strip",
|
|
||||||
Long: `Get or set the mute status of a specific strip.
|
|
||||||
|
|
||||||
If no argument is provided, the current mute status is retrieved.
|
|
||||||
If "true" or "1" is provided as an argument, the strip is muted.
|
|
||||||
If "false" or "0" is provided, the strip is unmuted.`,
|
|
||||||
Use: "mute [strip number] [true|false]",
|
|
||||||
Example: ` # Get the current mute status of strip 1
|
|
||||||
xair-cli strip mute 1
|
|
||||||
|
|
||||||
# Mute strip 1
|
|
||||||
xair-cli strip mute 1 true
|
|
||||||
# Unmute strip 1
|
|
||||||
xair-cli strip mute 1 false`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide a strip number")
|
|
||||||
}
|
|
||||||
|
|
||||||
stripIndex := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
if len(args) == 1 {
|
|
||||||
resp, err := client.Strip.Mute(stripIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting strip mute status: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Strip %d mute: %v\n", stripIndex, resp)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var muted bool
|
|
||||||
switch args[1] {
|
|
||||||
case "true", "1":
|
|
||||||
muted = true
|
|
||||||
case "false", "0":
|
|
||||||
muted = false
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Invalid mute status. Use true/false or 1/0")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := client.Strip.SetMute(stripIndex, muted)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting strip mute status: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if muted {
|
|
||||||
cmd.Printf("Strip %d muted successfully\n", stripIndex)
|
|
||||||
} else {
|
|
||||||
cmd.Printf("Strip %d unmuted successfully\n", stripIndex)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// stripFaderCmd represents the strip fader command.
|
|
||||||
var stripFaderCmd = &cobra.Command{
|
|
||||||
Short: "Get or set the fader level of a strip",
|
|
||||||
Long: `Get or set the fader level of a specific strip.
|
|
||||||
|
|
||||||
If no level argument is provided, the current fader level is retrieved.
|
|
||||||
If a level argument (in dB) is provided, the strip fader is set to that level.`,
|
|
||||||
Use: "fader [strip number] [level in dB]",
|
|
||||||
Example: ` # Get the current fader level of strip 1
|
|
||||||
xair-cli strip fader 1
|
|
||||||
|
|
||||||
# Set the fader level of strip 1 to -10.0 dB
|
|
||||||
xair-cli strip fader 1 -10.0`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide a strip number")
|
|
||||||
}
|
|
||||||
|
|
||||||
stripIndex := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
if len(args) == 1 {
|
|
||||||
level, err := client.Strip.Fader(stripIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting strip fader level: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Strip %d fader level: %.2f\n", stripIndex, level)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 2 {
|
|
||||||
return fmt.Errorf("Please provide a fader level in dB")
|
|
||||||
}
|
|
||||||
|
|
||||||
level := mustConvToFloat64(args[1])
|
|
||||||
|
|
||||||
err := client.Strip.SetFader(stripIndex, level)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting strip fader level: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Strip %d fader set to %.2f dB\n", stripIndex, level)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// stripFadeOutCmd represents the strip fade out command.
|
|
||||||
var stripFadeOutCmd = &cobra.Command{
|
|
||||||
Short: "Fade out the strip over a specified duration",
|
|
||||||
Long: "Fade out the strip over a specified duration in seconds.",
|
|
||||||
Use: "fadeout [strip number] --duration [seconds] [target level in dB]",
|
|
||||||
Example: ` # Fade out strip 1 over 5 seconds
|
|
||||||
xair-cli strip fadeout 1 --duration 5s -- -90.0`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide strip number")
|
|
||||||
}
|
|
||||||
|
|
||||||
stripIndex := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
duration, err := cmd.Flags().GetDuration("duration")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting duration flag: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
target := -90.0
|
|
||||||
if len(args) > 1 {
|
|
||||||
target = mustConvToFloat64(args[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
currentFader, err := client.Strip.Fader(stripIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting current strip fader level: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
totalSteps := float64(currentFader - target)
|
|
||||||
if totalSteps <= 0 {
|
|
||||||
cmd.Println("Strip is already at or below target level")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
stepDelay := time.Duration(duration.Seconds()*1000/totalSteps) * time.Millisecond
|
|
||||||
|
|
||||||
for currentFader > target {
|
|
||||||
currentFader -= 1.0
|
|
||||||
err := client.Strip.SetFader(stripIndex, currentFader)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting strip fader level: %w", err)
|
|
||||||
}
|
|
||||||
time.Sleep(stepDelay)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Strip %d faded out to %.2f dB over %.2f seconds\n", stripIndex, target, duration.Seconds())
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// stripFadeInCmd represents the strip fade in command.
|
|
||||||
var stripFadeInCmd = &cobra.Command{
|
|
||||||
Short: "Fade in the strip over a specified duration",
|
|
||||||
Long: "Fade in the strip over a specified duration in seconds.",
|
|
||||||
Use: "fadein [strip number] --duration [seconds] [target level in dB]",
|
|
||||||
Example: ` # Fade in strip 1 over 5 seconds
|
|
||||||
xair-cli strip fadein 1 --duration 5s 0`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide strip number")
|
|
||||||
}
|
|
||||||
|
|
||||||
stripIndex := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
duration, err := cmd.Flags().GetFloat64("duration")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting duration flag: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
target := 0.0
|
|
||||||
if len(args) > 1 {
|
|
||||||
target = mustConvToFloat64(args[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
currentFader, err := client.Strip.Fader(stripIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting current strip fader level: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
totalSteps := float64(target - currentFader)
|
|
||||||
if totalSteps <= 0 {
|
|
||||||
cmd.Println("Strip is already at or above target level")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
stepDelay := time.Duration(duration*1000/totalSteps) * time.Millisecond
|
|
||||||
|
|
||||||
for currentFader < target {
|
|
||||||
currentFader += 1.0
|
|
||||||
err := client.Strip.SetFader(stripIndex, currentFader)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting strip fader level: %w", err)
|
|
||||||
}
|
|
||||||
time.Sleep(stepDelay)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Printf("Strip %d faded in to %.2f dB over %.2f seconds\n", stripIndex, target, duration)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// stripSendCmd represents the strip send command.
|
|
||||||
var stripSendCmd = &cobra.Command{
|
|
||||||
Short: "Get or set the send levels for individual strips",
|
|
||||||
Long: "Get or set the send level from a specific strip to a specific bus.",
|
|
||||||
Use: "send [strip number] [bus number] [level in dB]",
|
|
||||||
Example: ` # Get the send level of strip 1 to bus 1
|
|
||||||
xair-cli strip send 1 1
|
|
||||||
|
|
||||||
# Set the send level of strip 1 to bus 1 to -5.0 dB
|
|
||||||
xair-cli strip send 1 1 -- -5.0`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 2 {
|
|
||||||
return fmt.Errorf("Please provide strip number and bus number")
|
|
||||||
}
|
|
||||||
|
|
||||||
stripIndex, busIndex := func() (int, int) {
|
|
||||||
return mustConvToInt(args[0]), mustConvToInt(args[1])
|
|
||||||
}()
|
|
||||||
|
|
||||||
if len(args) == 2 {
|
|
||||||
currentLevel, err := client.Strip.SendLevel(stripIndex, busIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting strip send level: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Strip %d send level to bus %d: %.2f dB\n", stripIndex, busIndex, currentLevel)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 3 {
|
|
||||||
return fmt.Errorf("Please provide a send level in dB")
|
|
||||||
}
|
|
||||||
|
|
||||||
level := mustConvToFloat64(args[2])
|
|
||||||
|
|
||||||
err := client.Strip.SetSendLevel(stripIndex, busIndex, level)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting strip send level: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Strip %d send level to bus %d set to %.2f dB\n", stripIndex, busIndex, level)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// stripNameCmd represents the strip name command.
|
|
||||||
var stripNameCmd = &cobra.Command{
|
|
||||||
Short: "Get or set the name of a strip",
|
|
||||||
Long: `Get or set the name of a specific strip.
|
|
||||||
|
|
||||||
If no name argument is provided, the current strip name is retrieved.
|
|
||||||
If a name argument is provided, the strip name is set to that value.`,
|
|
||||||
Use: "name [strip number] [name]",
|
|
||||||
Example: ` # Get the current name of strip 1
|
|
||||||
xair-cli strip name 1
|
|
||||||
|
|
||||||
# Set the name of strip 1 to "Guitar"
|
|
||||||
xair-cli strip name 1 "Guitar"`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
client := ClientFromContext(cmd.Context())
|
|
||||||
if client == nil {
|
|
||||||
return fmt.Errorf("OSC client not found in context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("Please provide a strip number")
|
|
||||||
}
|
|
||||||
|
|
||||||
stripIndex := mustConvToInt(args[0])
|
|
||||||
|
|
||||||
if len(args) == 1 {
|
|
||||||
name, err := client.Strip.Name(stripIndex)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error getting strip name: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Strip %d name: %s\n", stripIndex, name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
name := args[1]
|
|
||||||
|
|
||||||
err := client.Strip.SetName(stripIndex, name)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error setting strip name: %w", err)
|
|
||||||
}
|
|
||||||
cmd.Printf("Strip %d name set to: %s\n", stripIndex, name)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(stripCmd)
|
|
||||||
|
|
||||||
stripCmd.AddCommand(stripMuteCmd)
|
|
||||||
|
|
||||||
stripCmd.AddCommand(stripFaderCmd)
|
|
||||||
stripCmd.AddCommand(stripFadeOutCmd)
|
|
||||||
stripFadeOutCmd.Flags().DurationP("duration", "d", 5*time.Second, "Duration of the fade out in seconds")
|
|
||||||
stripCmd.AddCommand(stripFadeInCmd)
|
|
||||||
stripFadeInCmd.Flags().DurationP("duration", "d", 5*time.Second, "Duration of the fade in in seconds")
|
|
||||||
|
|
||||||
stripCmd.AddCommand(stripSendCmd)
|
|
||||||
|
|
||||||
stripCmd.AddCommand(stripNameCmd)
|
|
||||||
}
|
|
||||||
23
cmd/util.go
23
cmd/util.go
@@ -1,23 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// mustConvToFloat64 converts a string to float64, panicking on error.
|
|
||||||
func mustConvToFloat64(floatStr string) float64 {
|
|
||||||
level, err := strconv.ParseFloat(floatStr, 64)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return level
|
|
||||||
}
|
|
||||||
|
|
||||||
// mustConvToInt converts a string to int, panicking on error.
|
|
||||||
func mustConvToInt(intStr string) int {
|
|
||||||
val, err := strconv.Atoi(intStr)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
38
go.mod
38
go.mod
@@ -1,43 +1,35 @@
|
|||||||
module github.com/onyx-and-iris/xair-cli
|
module github.com/onyx-and-iris/xair-cli
|
||||||
|
|
||||||
go 1.24.2
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/alecthomas/kong v1.13.0
|
||||||
github.com/charmbracelet/log v0.4.2
|
github.com/charmbracelet/log v0.4.2
|
||||||
github.com/hypebeast/go-osc v0.0.0-20220308234300-cec5a8a1e5f5
|
github.com/hypebeast/go-osc v0.0.0-20220308234300-cec5a8a1e5f5
|
||||||
github.com/spf13/cobra v1.10.2
|
github.com/jotaen/kong-completion v0.0.11
|
||||||
github.com/spf13/viper v1.21.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/colorprofile v0.3.3 // indirect
|
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
||||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.10.3 // indirect
|
github.com/charmbracelet/x/ansi v0.11.4 // indirect
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
github.com/clipperhouse/displaywidth v0.4.1 // indirect
|
github.com/clipperhouse/displaywidth v0.9.0 // indirect
|
||||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
github.com/go-logfmt/logfmt v0.6.1 // indirect
|
||||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||||
github.com/muesli/termenv v0.16.0 // indirect
|
github.com/muesli/termenv v0.16.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/posener/complete v1.2.3 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect
|
||||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
|
||||||
github.com/spf13/afero v1.15.0 // indirect
|
|
||||||
github.com/spf13/cast v1.10.0 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.10 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
golang.org/x/sys v0.37.0 // indirect
|
|
||||||
golang.org/x/text v0.28.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
101
go.sum
101
go.sum
@@ -1,44 +1,48 @@
|
|||||||
|
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||||
|
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||||
|
github.com/alecthomas/kong v1.13.0 h1:5e/7XC3ugvhP1DQBmTS+WuHtCbcv44hsohMgcvVxSrA=
|
||||||
|
github.com/alecthomas/kong v1.13.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
|
||||||
|
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||||
|
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
github.com/charmbracelet/colorprofile v0.3.3 h1:DjJzJtLP6/NZ8p7Cgjno0CKGr7wwRJGxWUwh2IyhfAI=
|
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
|
||||||
github.com/charmbracelet/colorprofile v0.3.3/go.mod h1:nB1FugsAbzq284eJcjfah2nhdSLppN2NqvfotkfRYP4=
|
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
|
||||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||||
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
|
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
|
||||||
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
|
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
|
||||||
github.com/charmbracelet/x/ansi v0.10.3 h1:3WoV9XN8uMEnFRZZ+vBPRy59TaIWa+gJodS4Vg5Fut0=
|
github.com/charmbracelet/x/ansi v0.11.4 h1:6G65PLu6HjmE858CnTUQY1LXT3ZUWwfvqEROLF8vqHI=
|
||||||
github.com/charmbracelet/x/ansi v0.10.3/go.mod h1:uQt8bOrq/xgXjlGcFMc8U2WYbnxyjrKhnvTQluvfCaE=
|
github.com/charmbracelet/x/ansi v0.11.4/go.mod h1:/5AZ+UfWExW3int5H5ugnsG/PWjNcSQcwYsHBlPFQN4=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA=
|
||||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||||
github.com/clipperhouse/displaywidth v0.4.1 h1:uVw9V8UDfnggg3K2U84VWY1YLQ/x2aKSCtkRyYozfoU=
|
github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
|
||||||
github.com/clipperhouse/displaywidth v0.4.1/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
|
||||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
|
||||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
|
||||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
github.com/hypebeast/go-osc v0.0.0-20220308234300-cec5a8a1e5f5 h1:fqwINudmUrvGCuw+e3tedZ2UJ0hklSw6t8UPomctKyQ=
|
github.com/hypebeast/go-osc v0.0.0-20220308234300-cec5a8a1e5f5 h1:fqwINudmUrvGCuw+e3tedZ2UJ0hklSw6t8UPomctKyQ=
|
||||||
github.com/hypebeast/go-osc v0.0.0-20220308234300-cec5a8a1e5f5/go.mod h1:lqMjoCs0y0GoRRujSPZRBaGb4c5ER6TfkFKSClxkMbY=
|
github.com/hypebeast/go-osc v0.0.0-20220308234300-cec5a8a1e5f5/go.mod h1:lqMjoCs0y0GoRRujSPZRBaGb4c5ER6TfkFKSClxkMbY=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/jotaen/kong-completion v0.0.11 h1:ZRyQt+IwjcAObbiyxJZ3YR7r/o/f6HYidTK1+7YNtnE=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/jotaen/kong-completion v0.0.11/go.mod h1:dyIG20e3qq128SUBtF8jzI7YtkfzjWMlgbqkAJd6xHQ=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
@@ -47,47 +51,26 @@ github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byF
|
|||||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
||||||
|
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab h1:ZjX6I48eZSFetPb41dHudEyVr5v953N15TsNZXlkcWY=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
|
||||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
|
||||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
|
||||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
|
||||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
|
||||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
|
||||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
|
||||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
|
||||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
|
||||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
|
||||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
|
||||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
|
||||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
130
headamp.go
Normal file
130
headamp.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
package xair
|
package xair
|
||||||
|
|
||||||
var xairAddressMap = map[string]string{
|
var xairAddressMap = map[string]string{
|
||||||
"strip": "/ch/%02d",
|
"main": "/lr",
|
||||||
"bus": "/bus/%01d",
|
"strip": "/ch/%02d",
|
||||||
"headamp": "/headamp/%02d",
|
"bus": "/bus/%01d",
|
||||||
|
"headamp": "/headamp/%02d",
|
||||||
|
"snapshot": "/-snap",
|
||||||
}
|
}
|
||||||
|
|
||||||
var x32AddressMap = map[string]string{
|
var x32AddressMap = map[string]string{
|
||||||
"strip": "/ch/%02d",
|
"main": "/main/st",
|
||||||
"bus": "/bus/%02d",
|
"mainmono": "/main/mono",
|
||||||
"headamp": "/headamp/%02d",
|
"strip": "/ch/%02d",
|
||||||
|
"bus": "/bus/%02d",
|
||||||
|
"headamp": "/headamp/%02d",
|
||||||
|
"snapshot": "/-snap",
|
||||||
}
|
}
|
||||||
|
|
||||||
func addressMapForMixerKind(kind MixerKind) map[string]string {
|
func addressMapForMixerKind(kind MixerKind) map[string]string {
|
||||||
|
|||||||
@@ -4,13 +4,17 @@ import "fmt"
|
|||||||
|
|
||||||
type Bus struct {
|
type Bus struct {
|
||||||
baseAddress string
|
baseAddress string
|
||||||
client Client
|
client *Client
|
||||||
|
Eq *Eq
|
||||||
|
Comp *Comp
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBus(c Client) *Bus {
|
func NewBus(c *Client) *Bus {
|
||||||
return &Bus{
|
return &Bus{
|
||||||
baseAddress: c.addressMap["bus"],
|
baseAddress: c.addressMap["bus"],
|
||||||
client: c,
|
client: c,
|
||||||
|
Eq: newEqForBus(c),
|
||||||
|
Comp: newCompForBus(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,8 +26,11 @@ func (b *Bus) Mute(bus int) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-b.client.respChan
|
msg, err := b.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(int32)
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, fmt.Errorf("unexpected argument type for bus mute value")
|
return false, fmt.Errorf("unexpected argument type for bus mute value")
|
||||||
}
|
}
|
||||||
@@ -48,8 +55,11 @@ func (b *Bus) Fader(bus int) (float64, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-b.client.respChan
|
msg, err := b.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(float32)
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("unexpected argument type for bus fader value")
|
return 0, fmt.Errorf("unexpected argument type for bus fader value")
|
||||||
}
|
}
|
||||||
@@ -71,8 +81,11 @@ func (b *Bus) Name(bus int) (string, error) {
|
|||||||
return "", fmt.Errorf("failed to send bus name request: %v", err)
|
return "", fmt.Errorf("failed to send bus name request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-b.client.respChan
|
msg, err := b.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(string)
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("unexpected argument type for bus name value")
|
return "", fmt.Errorf("unexpected argument type for bus name value")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,20 @@ package xair
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
"github.com/charmbracelet/log"
|
||||||
|
|
||||||
"github.com/hypebeast/go-osc/osc"
|
"github.com/hypebeast/go-osc/osc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type parser interface {
|
|
||||||
Parse(data []byte) (*osc.Message, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
engine
|
engine
|
||||||
Main *Main
|
Main *Main
|
||||||
Strip *Strip
|
Strip *Strip
|
||||||
Bus *Bus
|
Bus *Bus
|
||||||
HeadAmp *HeadAmp
|
HeadAmp *HeadAmp
|
||||||
|
Snapshot *Snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new XAirClient instance
|
// NewClient creates a new XAirClient instance
|
||||||
@@ -43,6 +41,7 @@ func NewClient(mixerIP string, mixerPort int, opts ...Option) (*Client, error) {
|
|||||||
|
|
||||||
e := &engine{
|
e := &engine{
|
||||||
Kind: KindXAir,
|
Kind: KindXAir,
|
||||||
|
timeout: 100 * time.Millisecond,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
mixerAddr: mixerAddr,
|
mixerAddr: mixerAddr,
|
||||||
parser: newParser(),
|
parser: newParser(),
|
||||||
@@ -58,10 +57,11 @@ func NewClient(mixerIP string, mixerPort int, opts ...Option) (*Client, error) {
|
|||||||
c := &Client{
|
c := &Client{
|
||||||
engine: *e,
|
engine: *e,
|
||||||
}
|
}
|
||||||
c.Main = newMain(*c)
|
c.Main = newMainStereo(c)
|
||||||
c.Strip = NewStrip(*c)
|
c.Strip = NewStrip(c)
|
||||||
c.Bus = NewBus(*c)
|
c.Bus = NewBus(c)
|
||||||
c.HeadAmp = NewHeadAmp(*c)
|
c.HeadAmp = NewHeadAmp(c)
|
||||||
|
c.Snapshot = NewSnapshot(c)
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
@@ -72,8 +72,8 @@ func (c *Client) StartListening() {
|
|||||||
log.Debugf("Started listening on %s...", c.engine.conn.LocalAddr().String())
|
log.Debugf("Started listening on %s...", c.engine.conn.LocalAddr().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the client and closes the connection
|
// Close stops the client and closes the connection
|
||||||
func (c *Client) Stop() {
|
func (c *Client) Close() {
|
||||||
close(c.engine.done)
|
close(c.engine.done)
|
||||||
if c.engine.conn != nil {
|
if c.engine.conn != nil {
|
||||||
c.engine.conn.Close()
|
c.engine.conn.Close()
|
||||||
@@ -85,21 +85,38 @@ func (c *Client) SendMessage(address string, args ...any) error {
|
|||||||
return c.engine.sendToAddress(c.mixerAddr, address, args...)
|
return c.engine.sendToAddress(c.mixerAddr, address, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReceiveMessage receives an OSC message from the mixer
|
||||||
|
func (c *Client) ReceiveMessage() (*osc.Message, error) {
|
||||||
|
t := time.Tick(c.engine.timeout)
|
||||||
|
select {
|
||||||
|
case <-t:
|
||||||
|
return nil, fmt.Errorf("timeout waiting for response")
|
||||||
|
case msg := <-c.respChan:
|
||||||
|
if msg == nil {
|
||||||
|
return nil, fmt.Errorf("no message received")
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RequestInfo requests mixer information
|
// RequestInfo requests mixer information
|
||||||
func (c *Client) RequestInfo() (error, InfoResponse) {
|
func (c *Client) RequestInfo() (InfoResponse, error) {
|
||||||
|
var info InfoResponse
|
||||||
err := c.SendMessage("/xinfo")
|
err := c.SendMessage("/xinfo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, InfoResponse{}
|
return info, err
|
||||||
}
|
}
|
||||||
|
|
||||||
val := <-c.respChan
|
msg, err := c.ReceiveMessage()
|
||||||
var info InfoResponse
|
if err != nil {
|
||||||
if len(val.Arguments) >= 3 {
|
return info, err
|
||||||
info.Host = val.Arguments[0].(string)
|
|
||||||
info.Name = val.Arguments[1].(string)
|
|
||||||
info.Model = val.Arguments[2].(string)
|
|
||||||
}
|
}
|
||||||
return nil, info
|
if len(msg.Arguments) >= 3 {
|
||||||
|
info.Host = msg.Arguments[0].(string)
|
||||||
|
info.Name = msg.Arguments[1].(string)
|
||||||
|
info.Model = msg.Arguments[2].(string)
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeepAlive sends keep-alive message (required for multi-client usage)
|
// KeepAlive sends keep-alive message (required for multi-client usage)
|
||||||
|
|||||||
261
internal/xair/comp.go
Normal file
261
internal/xair/comp.go
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
package xair
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Comp struct {
|
||||||
|
client *Client
|
||||||
|
baseAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory function to create Comp instance for Strip
|
||||||
|
func newCompForStrip(c *Client) *Comp {
|
||||||
|
return &Comp{
|
||||||
|
client: c,
|
||||||
|
baseAddress: c.addressMap["strip"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory function to create Comp instance for Bus
|
||||||
|
func newCompForBus(c *Client) *Comp {
|
||||||
|
return &Comp{
|
||||||
|
client: c,
|
||||||
|
baseAddress: c.addressMap["bus"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On retrieves the on/off status of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) On(index int) (bool, error) {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/on"
|
||||||
|
err := c.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := c.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Errorf("unexpected argument type for Compressor on value")
|
||||||
|
}
|
||||||
|
return val != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOn sets the on/off status of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) SetOn(index int, on bool) error {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/on"
|
||||||
|
var value int32
|
||||||
|
if on {
|
||||||
|
value = 1
|
||||||
|
}
|
||||||
|
return c.client.SendMessage(address, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode retrieves the current mode of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) Mode(index int) (string, error) {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/mode"
|
||||||
|
err := c.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleModes := []string{"comp", "exp"}
|
||||||
|
|
||||||
|
msg, err := c.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("unexpected argument type for Compressor mode value")
|
||||||
|
}
|
||||||
|
return possibleModes[val], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMode sets the mode of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) SetMode(index int, mode string) error {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/mode"
|
||||||
|
possibleModes := []string{"comp", "exp"}
|
||||||
|
return c.client.SendMessage(address, int32(indexOf(possibleModes, mode)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Threshold retrieves the threshold value of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) Threshold(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/thr"
|
||||||
|
err := c.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := c.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Compressor threshold value")
|
||||||
|
}
|
||||||
|
return linGet(-60, 0, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetThreshold sets the threshold value of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) SetThreshold(index int, threshold float64) error {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/thr"
|
||||||
|
return c.client.SendMessage(address, float32(linSet(-60, 0, threshold)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ratio retrieves the ratio value of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) Ratio(index int) (float32, error) {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/ratio"
|
||||||
|
err := c.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleValues := []float32{1.1, 1.3, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 7.0, 10, 20, 100}
|
||||||
|
|
||||||
|
msg, err := c.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Compressor ratio value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return possibleValues[val], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRatio sets the ratio value of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) SetRatio(index int, ratio float64) error {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/ratio"
|
||||||
|
possibleValues := []float32{1.1, 1.3, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 7.0, 10, 20, 100}
|
||||||
|
|
||||||
|
return c.client.SendMessage(address, int32(indexOf(possibleValues, float32(ratio))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attack retrieves the attack time of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) Attack(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/attack"
|
||||||
|
err := c.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := c.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Compressor attack value")
|
||||||
|
}
|
||||||
|
return linGet(0, 120, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAttack sets the attack time of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) SetAttack(index int, attack float64) error {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/attack"
|
||||||
|
return c.client.SendMessage(address, float32(linSet(0, 120, attack)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hold retrieves the hold time of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) Hold(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/hold"
|
||||||
|
err := c.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := c.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Compressor hold value")
|
||||||
|
}
|
||||||
|
return logGet(0.02, 2000, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHold sets the hold time of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) SetHold(index int, hold float64) error {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/hold"
|
||||||
|
return c.client.SendMessage(address, float32(logSet(0.02, 2000, hold)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release retrieves the release time of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) Release(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/release"
|
||||||
|
err := c.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := c.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Compressor release value")
|
||||||
|
}
|
||||||
|
return logGet(4, 4000, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRelease sets the release time of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) SetRelease(index int, release float64) error {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/release"
|
||||||
|
return c.client.SendMessage(address, float32(logSet(4, 4000, release)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makeup retrieves the makeup gain of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) Makeup(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/mgain"
|
||||||
|
err := c.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := c.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Compressor makeup gain value")
|
||||||
|
}
|
||||||
|
return linGet(0, 24, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMakeup sets the makeup gain of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) SetMakeup(index int, makeup float64) error {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/mgain"
|
||||||
|
return c.client.SendMessage(address, float32(linSet(0, 24, makeup)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mix retrieves the mix value of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) Mix(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/mix"
|
||||||
|
err := c.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := c.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Compressor mix value")
|
||||||
|
}
|
||||||
|
return linGet(0, 100, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMix sets the mix value of the Compressor for a specific strip or bus (1-based indexing).
|
||||||
|
func (c *Comp) SetMix(index int, mix float64) error {
|
||||||
|
address := fmt.Sprintf(c.baseAddress, index) + "/dyn/mix"
|
||||||
|
return c.client.SendMessage(address, float32(linSet(0, 100, mix)))
|
||||||
|
}
|
||||||
@@ -9,8 +9,13 @@ import (
|
|||||||
"github.com/hypebeast/go-osc/osc"
|
"github.com/hypebeast/go-osc/osc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type parser interface {
|
||||||
|
Parse(data []byte) (*osc.Message, error)
|
||||||
|
}
|
||||||
|
|
||||||
type engine struct {
|
type engine struct {
|
||||||
Kind MixerKind
|
Kind MixerKind
|
||||||
|
timeout time.Duration
|
||||||
conn *net.UDPConn
|
conn *net.UDPConn
|
||||||
mixerAddr *net.UDPAddr
|
mixerAddr *net.UDPAddr
|
||||||
|
|
||||||
@@ -30,7 +35,7 @@ func (e *engine) receiveLoop() {
|
|||||||
case <-e.done:
|
case <-e.done:
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
// Set read timeout to avoid blocking forever
|
// Set a short read deadline to prevent blocking indefinitely
|
||||||
e.conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
|
e.conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
|
||||||
n, _, err := e.conn.ReadFromUDP(buffer)
|
n, _, err := e.conn.ReadFromUDP(buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
182
internal/xair/eq.go
Normal file
182
internal/xair/eq.go
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
package xair
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Eq struct {
|
||||||
|
client *Client
|
||||||
|
baseAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory function to create Eq instance for Strip
|
||||||
|
func newEqForStrip(c *Client) *Eq {
|
||||||
|
return &Eq{
|
||||||
|
client: c,
|
||||||
|
baseAddress: c.addressMap["strip"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory function to create Eq instance for Bus
|
||||||
|
func newEqForBus(c *Client) *Eq {
|
||||||
|
return &Eq{
|
||||||
|
client: c,
|
||||||
|
baseAddress: c.addressMap["bus"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On retrieves the on/off status of the EQ for a specific strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) On(index int) (bool, error) {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + "/eq/on"
|
||||||
|
err := e.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := e.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Errorf("unexpected argument type for EQ on value")
|
||||||
|
}
|
||||||
|
return val != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOn sets the on/off status of the EQ for a specific strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) SetOn(index int, on bool) error {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + "/eq/on"
|
||||||
|
var value int32
|
||||||
|
if on {
|
||||||
|
value = 1
|
||||||
|
}
|
||||||
|
return e.client.SendMessage(address, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Eq) Mode(index int) (string, error) {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + "/eq/mode"
|
||||||
|
err := e.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleModes := []string{"peq", "geq", "teq"}
|
||||||
|
|
||||||
|
msg, err := e.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("unexpected argument type for EQ mode value")
|
||||||
|
}
|
||||||
|
return possibleModes[val], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Eq) SetMode(index int, mode string) error {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + "/eq/mode"
|
||||||
|
possibleModes := []string{"peq", "geq", "teq"}
|
||||||
|
return e.client.SendMessage(address, int32(indexOf(possibleModes, mode)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gain retrieves the gain for a specific EQ band on a strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) Gain(index int, band int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + fmt.Sprintf("/eq/%d/g", band)
|
||||||
|
err := e.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := e.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for EQ gain value")
|
||||||
|
}
|
||||||
|
return linGet(-15, 15, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGain sets the gain for a specific EQ band on a strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) SetGain(index int, band int, gain float64) error {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + fmt.Sprintf("/eq/%d/g", band)
|
||||||
|
return e.client.SendMessage(address, float32(linSet(-15, 15, gain)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frequency retrieves the frequency for a specific EQ band on a strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) Frequency(index int, band int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + fmt.Sprintf("/eq/%d/f", band)
|
||||||
|
err := e.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := e.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for EQ frequency value")
|
||||||
|
}
|
||||||
|
return logGet(20, 20000, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFrequency sets the frequency for a specific EQ band on a strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) SetFrequency(index int, band int, frequency float64) error {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + fmt.Sprintf("/eq/%d/f", band)
|
||||||
|
return e.client.SendMessage(address, float32(logSet(20, 20000, frequency)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Q retrieves the Q factor for a specific EQ band on a strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) Q(index int, band int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + fmt.Sprintf("/eq/%d/q", band)
|
||||||
|
err := e.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := e.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for EQ Q value")
|
||||||
|
}
|
||||||
|
return logGet(0.3, 10, 1.0-float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetQ sets the Q factor for a specific EQ band on a strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) SetQ(index int, band int, q float64) error {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + fmt.Sprintf("/eq/%d/q", band)
|
||||||
|
return e.client.SendMessage(address, float32(1.0-logSet(0.3, 10, q)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type retrieves the type for a specific EQ band on a strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) Type(index int, band int) (string, error) {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + fmt.Sprintf("/eq/%d/type", band)
|
||||||
|
err := e.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleTypes := []string{"lcut", "lshv", "peq", "veq", "hshv", "hcut"}
|
||||||
|
|
||||||
|
msg, err := e.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("unexpected argument type for EQ type value")
|
||||||
|
}
|
||||||
|
return possibleTypes[val], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetType sets the type for a specific EQ band on a strip or bus (1-based indexing).
|
||||||
|
func (e *Eq) SetType(index int, band int, eqType string) error {
|
||||||
|
address := fmt.Sprintf(e.baseAddress, index) + fmt.Sprintf("/eq/%d/type", band)
|
||||||
|
possibleTypes := []string{"lcut", "lshv", "peq", "veq", "hshv", "hcut"}
|
||||||
|
return e.client.SendMessage(address, int32(indexOf(possibleTypes, eqType)))
|
||||||
|
}
|
||||||
195
internal/xair/gate.go
Normal file
195
internal/xair/gate.go
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
package xair
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Gate struct {
|
||||||
|
client *Client
|
||||||
|
baseAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGate(c *Client) *Gate {
|
||||||
|
return &Gate{client: c, baseAddress: c.addressMap["strip"]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On retrieves the on/off status of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) On(index int) (bool, error) {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/on"
|
||||||
|
err := g.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := g.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Errorf("unexpected argument type for Gate on value")
|
||||||
|
}
|
||||||
|
return val != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOn sets the on/off status of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) SetOn(index int, on bool) error {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/on"
|
||||||
|
var value int32
|
||||||
|
if on {
|
||||||
|
value = 1
|
||||||
|
}
|
||||||
|
return g.client.SendMessage(address, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode retrieves the current mode of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) Mode(index int) (string, error) {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/mode"
|
||||||
|
err := g.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleModes := []string{"exp2", "exp3", "exp4", "gate", "duck"}
|
||||||
|
|
||||||
|
msg, err := g.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("unexpected argument type for Gate mode value")
|
||||||
|
}
|
||||||
|
return possibleModes[val], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMode sets the mode of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) SetMode(index int, mode string) error {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/mode"
|
||||||
|
possibleModes := []string{"exp2", "exp3", "exp4", "gate", "duck"}
|
||||||
|
|
||||||
|
return g.client.SendMessage(address, int32(indexOf(possibleModes, mode)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Threshold retrieves the threshold value of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) Threshold(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/thr"
|
||||||
|
err := g.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := g.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Gate threshold value")
|
||||||
|
}
|
||||||
|
return linGet(-80, 0, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetThreshold sets the threshold value of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) SetThreshold(index int, threshold float64) error {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/thr"
|
||||||
|
return g.client.SendMessage(address, float32(linSet(-80, 0, threshold)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range retrieves the range value of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) Range(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/range"
|
||||||
|
err := g.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := g.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Gate range value")
|
||||||
|
}
|
||||||
|
return linGet(3, 60, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRange sets the range value of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) SetRange(index int, rangeVal float64) error {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/range"
|
||||||
|
return g.client.SendMessage(address, float32(linSet(3, 60, rangeVal)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attack retrieves the attack time of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) Attack(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/attack"
|
||||||
|
err := g.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := g.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Gate attack value")
|
||||||
|
}
|
||||||
|
return linGet(0, 120, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAttack sets the attack time of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) SetAttack(index int, attack float64) error {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/attack"
|
||||||
|
return g.client.SendMessage(address, float32(linSet(0, 120, attack)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hold retrieves the hold time of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) Hold(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/hold"
|
||||||
|
err := g.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := g.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Gate hold value")
|
||||||
|
}
|
||||||
|
return logGet(0.02, 2000, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHold sets the hold time of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) SetHold(index int, hold float64) error {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/hold"
|
||||||
|
return g.client.SendMessage(address, float32(logSet(0.02, 2000, hold)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release retrieves the release time of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) Release(index int) (float64, error) {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/release"
|
||||||
|
err := g.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := g.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected argument type for Gate release value")
|
||||||
|
}
|
||||||
|
return logGet(5, 4000, float64(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRelease sets the release time of the Gate for a specific strip (1-based indexing).
|
||||||
|
func (g *Gate) SetRelease(index int, release float64) error {
|
||||||
|
address := fmt.Sprintf(g.baseAddress, index) + "/gate/release"
|
||||||
|
return g.client.SendMessage(address, float32(logSet(5, 4000, release)))
|
||||||
|
}
|
||||||
@@ -4,10 +4,10 @@ import "fmt"
|
|||||||
|
|
||||||
type HeadAmp struct {
|
type HeadAmp struct {
|
||||||
baseAddress string
|
baseAddress string
|
||||||
client Client
|
client *Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHeadAmp(c Client) *HeadAmp {
|
func NewHeadAmp(c *Client) *HeadAmp {
|
||||||
return &HeadAmp{
|
return &HeadAmp{
|
||||||
baseAddress: c.addressMap["headamp"],
|
baseAddress: c.addressMap["headamp"],
|
||||||
client: c,
|
client: c,
|
||||||
@@ -22,8 +22,11 @@ func (h *HeadAmp) Gain(index int) (float64, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-h.client.respChan
|
msg, err := h.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(float32)
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("unexpected argument type for headamp gain value")
|
return 0, fmt.Errorf("unexpected argument type for headamp gain value")
|
||||||
}
|
}
|
||||||
@@ -45,8 +48,11 @@ func (h *HeadAmp) PhantomPower(index int) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-h.client.respChan
|
msg, err := h.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(int32)
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, fmt.Errorf("unexpected argument type for phantom power value")
|
return false, fmt.Errorf("unexpected argument type for phantom power value")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,39 @@ package xair
|
|||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
type Main struct {
|
type Main struct {
|
||||||
client Client
|
baseAddress string
|
||||||
|
client *Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMain(c Client) *Main {
|
func newMainStereo(c *Client) *Main {
|
||||||
return &Main{
|
return &Main{
|
||||||
client: c,
|
baseAddress: c.addressMap["main"],
|
||||||
|
client: c,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Still considering the best way to implement main mono support.
|
||||||
|
func newMainMono(c *Client) *Main {
|
||||||
|
return &Main{
|
||||||
|
baseAddress: c.addressMap["mainmono"],
|
||||||
|
client: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Fader requests the current main L/R fader level
|
// Fader requests the current main L/R fader level
|
||||||
func (m *Main) Fader() (float64, error) {
|
func (m *Main) Fader() (float64, error) {
|
||||||
err := m.client.SendMessage("/lr/mix/fader")
|
address := m.baseAddress + "/mix/fader"
|
||||||
|
err := m.client.SendMessage(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-m.client.respChan
|
msg, err := m.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(float32)
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("unexpected argument type for main LR fader value")
|
return 0, fmt.Errorf("unexpected argument type for main LR fader value")
|
||||||
}
|
}
|
||||||
@@ -29,18 +44,23 @@ func (m *Main) Fader() (float64, error) {
|
|||||||
|
|
||||||
// SetFader sets the main L/R fader level
|
// SetFader sets the main L/R fader level
|
||||||
func (m *Main) SetFader(level float64) error {
|
func (m *Main) SetFader(level float64) error {
|
||||||
return m.client.SendMessage("/lr/mix/fader", float32(mustDbInto(level)))
|
address := m.baseAddress + "/mix/fader"
|
||||||
|
return m.client.SendMessage(address, float32(mustDbInto(level)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mute requests the current main L/R mute status
|
// Mute requests the current main L/R mute status
|
||||||
func (m *Main) Mute() (bool, error) {
|
func (m *Main) Mute() (bool, error) {
|
||||||
err := m.client.SendMessage("/lr/mix/on")
|
address := m.baseAddress + "/mix/on"
|
||||||
|
err := m.client.SendMessage(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-m.client.respChan
|
msg, err := m.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(int32)
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, fmt.Errorf("unexpected argument type for main LR mute value")
|
return false, fmt.Errorf("unexpected argument type for main LR mute value")
|
||||||
}
|
}
|
||||||
@@ -49,9 +69,10 @@ func (m *Main) Mute() (bool, error) {
|
|||||||
|
|
||||||
// SetMute sets the main L/R mute status
|
// SetMute sets the main L/R mute status
|
||||||
func (m *Main) SetMute(muted bool) error {
|
func (m *Main) SetMute(muted bool) error {
|
||||||
|
address := m.baseAddress + "/mix/on"
|
||||||
var value int32
|
var value int32
|
||||||
if !muted {
|
if !muted {
|
||||||
value = 1
|
value = 1
|
||||||
}
|
}
|
||||||
return m.client.SendMessage("/lr/mix/on", value)
|
return m.client.SendMessage(address, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package xair
|
package xair
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type Option func(*engine)
|
type Option func(*engine)
|
||||||
|
|
||||||
func WithKind(kind string) Option {
|
func WithKind(kind string) Option {
|
||||||
@@ -8,3 +10,9 @@ func WithKind(kind string) Option {
|
|||||||
e.addressMap = addressMapForMixerKind(e.Kind)
|
e.addressMap = addressMapForMixerKind(e.Kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithTimeout(timeout time.Duration) Option {
|
||||||
|
return func(e *engine) {
|
||||||
|
e.timeout = timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
64
internal/xair/snapshot.go
Normal file
64
internal/xair/snapshot.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package xair
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Snapshot struct {
|
||||||
|
baseAddress string
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSnapshot(c *Client) *Snapshot {
|
||||||
|
return &Snapshot{
|
||||||
|
baseAddress: c.addressMap["snapshot"],
|
||||||
|
client: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name gets the name of the snapshot at the given index.
|
||||||
|
func (s *Snapshot) Name(index int) (string, error) {
|
||||||
|
address := s.baseAddress + fmt.Sprintf("/%02d/name", index)
|
||||||
|
err := s.client.SendMessage(address)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := s.client.ReceiveMessage()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
name, ok := msg.Arguments[0].(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("unexpected argument type for snapshot name")
|
||||||
|
}
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetName sets the name of the snapshot at the given index.
|
||||||
|
func (s *Snapshot) SetName(index int, name string) error {
|
||||||
|
address := s.baseAddress + fmt.Sprintf("/%02d/name", index)
|
||||||
|
return s.client.SendMessage(address, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentName sets the name of the current snapshot.
|
||||||
|
func (s *Snapshot) CurrentName(name string) error {
|
||||||
|
address := s.baseAddress + "/name"
|
||||||
|
return s.client.SendMessage(address, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentLoad loads the snapshot at the given index.
|
||||||
|
func (s *Snapshot) CurrentLoad(index int) error {
|
||||||
|
address := s.baseAddress + "/load"
|
||||||
|
return s.client.SendMessage(address, int32(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentSave saves the current state to the snapshot at the given index.
|
||||||
|
func (s *Snapshot) CurrentSave(index int) error {
|
||||||
|
address := s.baseAddress + "/save"
|
||||||
|
return s.client.SendMessage(address, int32(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentDelete deletes the snapshot at the given index.
|
||||||
|
func (s *Snapshot) CurrentDelete(index int) error {
|
||||||
|
address := s.baseAddress + "/delete"
|
||||||
|
return s.client.SendMessage(address, int32(index))
|
||||||
|
}
|
||||||
@@ -4,13 +4,19 @@ import "fmt"
|
|||||||
|
|
||||||
type Strip struct {
|
type Strip struct {
|
||||||
baseAddress string
|
baseAddress string
|
||||||
client Client
|
client *Client
|
||||||
|
Gate *Gate
|
||||||
|
Eq *Eq
|
||||||
|
Comp *Comp
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStrip(c Client) *Strip {
|
func NewStrip(c *Client) *Strip {
|
||||||
return &Strip{
|
return &Strip{
|
||||||
baseAddress: c.addressMap["strip"],
|
baseAddress: c.addressMap["strip"],
|
||||||
client: c,
|
client: c,
|
||||||
|
Gate: newGate(c),
|
||||||
|
Eq: newEqForStrip(c),
|
||||||
|
Comp: newCompForStrip(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,8 +28,11 @@ func (s *Strip) Mute(index int) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-s.client.respChan
|
msg, err := s.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(int32)
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, fmt.Errorf("unexpected argument type for strip mute value")
|
return false, fmt.Errorf("unexpected argument type for strip mute value")
|
||||||
}
|
}
|
||||||
@@ -48,8 +57,11 @@ func (s *Strip) Fader(strip int) (float64, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-s.client.respChan
|
msg, err := s.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(float32)
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("unexpected argument type for fader value")
|
return 0, fmt.Errorf("unexpected argument type for fader value")
|
||||||
}
|
}
|
||||||
@@ -71,8 +83,11 @@ func (s *Strip) Name(strip int) (string, error) {
|
|||||||
return "", fmt.Errorf("failed to send strip name request: %v", err)
|
return "", fmt.Errorf("failed to send strip name request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-s.client.respChan
|
msg, err := s.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(string)
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("unexpected argument type for strip name value")
|
return "", fmt.Errorf("unexpected argument type for strip name value")
|
||||||
}
|
}
|
||||||
@@ -93,8 +108,11 @@ func (s *Strip) Color(strip int) (int32, error) {
|
|||||||
return 0, fmt.Errorf("failed to send strip color request: %v", err)
|
return 0, fmt.Errorf("failed to send strip color request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-s.client.respChan
|
msg, err := s.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(int32)
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(int32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("unexpected argument type for strip color value")
|
return 0, fmt.Errorf("unexpected argument type for strip color value")
|
||||||
}
|
}
|
||||||
@@ -115,8 +133,11 @@ func (s *Strip) SendLevel(strip int, bus int) (float64, error) {
|
|||||||
return 0, fmt.Errorf("failed to send strip send level request: %v", err)
|
return 0, fmt.Errorf("failed to send strip send level request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := <-s.client.respChan
|
msg, err := s.client.ReceiveMessage()
|
||||||
val, ok := resp.Arguments[0].(float32)
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, ok := msg.Arguments[0].(float32)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("unexpected argument type for strip send level value")
|
return 0, fmt.Errorf("unexpected argument type for strip send level value")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ func linSet(min float64, max float64, value float64) float64 {
|
|||||||
return (value - min) / (max - min)
|
return (value - min) / (max - min)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func logGet(min float64, max float64, value float64) float64 {
|
||||||
|
return min * math.Exp(math.Log(max/min)*value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logSet(min float64, max float64, value float64) float64 {
|
||||||
|
return math.Log(value/min) / math.Log(max/min)
|
||||||
|
}
|
||||||
|
|
||||||
func mustDbInto(db float64) float64 {
|
func mustDbInto(db float64) float64 {
|
||||||
switch {
|
switch {
|
||||||
case db >= 10:
|
case db >= 10:
|
||||||
@@ -48,3 +56,13 @@ func toFixed(num float64, precision int) float64 {
|
|||||||
output := math.Pow(10, float64(precision))
|
output := math.Pow(10, float64(precision))
|
||||||
return float64(math.Round(num*output)) / output
|
return float64(math.Round(num*output)) / output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generic indexOf returns the index of elem in slice, or -1 if not found.
|
||||||
|
func indexOf[T comparable](slice []T, elem T) int {
|
||||||
|
for i, v := range slice {
|
||||||
|
if v == elem {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|||||||
129
lr.go
Normal file
129
lr.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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:""`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *bool `arg:"" help:"The mute state to set. If not provided, the current state will be printed." optional:""`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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); 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: %t\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
|
||||||
|
}
|
||||||
128
main.go
128
main.go
@@ -1,7 +1,131 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/onyx-and-iris/xair-cli/cmd"
|
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("xair-cli version: %s\n", vars["version"])
|
||||||
|
app.Exit(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type context struct {
|
||||||
|
Client *xair.Client
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 xair-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"`
|
||||||
|
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() {
|
func main() {
|
||||||
cmd.Execute()
|
var cli CLI
|
||||||
|
kongcompletion.Register(kong.Must(&cli))
|
||||||
|
ctx := kong.Parse(
|
||||||
|
&cli,
|
||||||
|
kong.Name("xair-cli"),
|
||||||
|
kong.Description("A CLI to control Behringer X-Air 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 X-Air 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 X-Air 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 X-Air client based on the provided configuration.
|
||||||
|
func connect(config Config) (*xair.Client, error) {
|
||||||
|
client, err := xair.NewClient(
|
||||||
|
config.Host,
|
||||||
|
config.Port,
|
||||||
|
xair.WithKind(config.Kind),
|
||||||
|
xair.WithTimeout(config.Timeout),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|||||||
39
raw.go
Normal file
39
raw.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
75
snapshot.go
Normal file
75
snapshot.go
Normal file
@@ -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." cmd:"save"`
|
||||||
|
Load LoadCmd `help:"Load a mixer state." 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)
|
||||||
|
}
|
||||||
723
strip.go
Normal file
723
strip.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user