32 Commits

Author SHA1 Message Date
b8b637b4b7 upd snapshot subcommand help output 2026-02-06 17:47:21 +00:00
52d51a7094 reword 2026-02-06 17:45:30 +00:00
09a4e9fbfc upd Configuration in README 2026-02-06 17:45:12 +00:00
04f7319776 add loglevel to Configuration section 2026-02-06 17:37:57 +00:00
0c67887ad7 upd README 2026-02-06 17:31:50 +00:00
4db173154b snapshot commands implemented 2026-02-06 17:31:41 +00:00
27922d41bb --loglevel flag added to the root command 2026-02-06 17:31:36 +00:00
e21fd59564 reword 2026-02-06 15:30:18 +00:00
2d829e28e1 upd README 2026-02-06 11:28:52 +00:00
3f8861ded2 timeout now a flag on the root command.
It applies to all messages sent

new option function WithTimeout added
2026-02-06 11:28:40 +00:00
1ad214ba4a add validation to --kind flag 2026-02-06 00:52:12 +00:00
66da965edd fix default value for kind 2026-02-06 00:47:34 +00:00
079a0b240d ensure all optional args are marked as optional 2026-02-06 00:41:26 +00:00
fe711f79f1 Main struct now uses address map 2026-02-06 00:41:06 +00:00
c94ac62cb8 upd address maps 2026-02-06 00:40:52 +00:00
5933b25114 move parser interface into engine.go
add snapshot field to Client
2026-02-06 00:40:42 +00:00
fa704832d5 reword 2026-02-05 21:44:39 +00:00
69925021af spell fix 2026-02-05 15:43:19 +00:00
e092ed3c4e add Configuration section to README 2026-02-05 15:42:39 +00:00
d87bc2678c add short Config flags 2026-02-05 15:42:07 +00:00
c3221f3df5 reduce default timeout to 100ms
add env var for raw timeout
2026-02-05 15:13:33 +00:00
dc733ba500 add macos target to Taskfile 2026-02-05 14:20:03 +00:00
2a5e0e022f upd README 2026-02-05 13:42:57 +00:00
663a863386 fix main fadein/fadeout help output 2026-02-05 13:23:36 +00:00
70889c47bc upd README 2026-02-05 13:20:20 +00:00
2ad9f2fd6a add docstrings
upd help messages
2026-02-05 13:20:05 +00:00
c20aa82e3b remove redundant util function 2026-02-05 04:15:21 +00:00
1c5a75ffe1 add bus eq band 3 gain example 2026-02-05 04:06:08 +00:00
b075515654 upd README 2026-02-05 04:02:01 +00:00
139e092937 raw doesn't need to be a command group
fix `nil pointer deference` for set commands
2026-02-05 04:01:34 +00:00
8539685770 upd README 2026-02-05 03:55:08 +00:00
8fc5a55eff fixes bug where strip/bus eq on would try to validate the band number. 2026-02-05 03:54:53 +00:00
22 changed files with 687 additions and 223 deletions

169
README.md
View File

@@ -6,35 +6,129 @@
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.
- --loglevel/-L: The application's logging verbosity.
Pass `--host` and any other configuration as flags on the root commmand:
```console
xair-cli --host mixer.local --kind xair --timeout 50ms --help
```
#### Environment Variables
Or you may load them from your environment:
Example .envrc:
```bash
#!/usr/bin/env bash
XAIR_CLI_HOST=mixer.local
XAIR_CLI_PORT=10024
XAIR_CLI_KIND=xair
XAIR_CLI_TIMEOUT=100ms
XAIR_CLI_LOGLEVEL=warn
```
### 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
raw Send a raw OSC message to the mixer
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 to a snapshot.
snapshot <index> load Load a mixer state from a snapshot.
snapshot <index> delete Delete a snapshot.
Run "xair-cli <command> --help" for more information on a command.
``` ```
### Examples ### Examples
@@ -45,36 +139,47 @@ Use "xair-cli [command] --help" for more information about a command.
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 phantom 9 on xair-cli headamp 9 phantom on
xair-cli headamp gain 9 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* *enable eq for strip 01*
```console ```console
xair-cli strip eq on 1 true xair-cli strip 1 eq on true
``` ```
*rename bus 01 to 'vocal mix'* *rename bus 01 to 'vocal mix'*
```console ```console
xair-cli bus name 1 'vocal mix' xair-cli bus 1 name 'vocal mix'
``` ```
For every command/subcommand there exists a `--help` flag which you can use to get usage information. *set bus 03 eq band 03 (LoMid) gain*
```console
xair-cli bus 3 eq 3 gain -- -3.5
```
*Send a raw OSC message to the mixer*
```console
xair-cli raw /xinfo
### Notes xair-cli raw /ch/01/config/name 'rode podmic'
xair-cli raw /ch/01/config/name
This CLI is useful if just want to run some commands on the terminal using a single binary, no further downloads. However, there exists an alternative you should check out that has wider support of the XAir OSC protocol including support for operations like batch commands and network discovery (which I have no plans to implement on this CLI). Check out [dc-xair-cli](https://pypi.org/project/dc-xair-cli/) on pypi. ```
### 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).

View File

@@ -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:

106
bus.go
View File

@@ -3,8 +3,11 @@ package main
import ( import (
"fmt" "fmt"
"time" "time"
"github.com/alecthomas/kong"
) )
// BusCmdGroup defines the commands related to controlling the buses of the X-Air device.
type BusCmdGroup struct { type BusCmdGroup struct {
Index struct { Index struct {
Index int `arg:"" help:"The index of the bus. (1-based indexing)"` Index int `arg:"" help:"The index of the bus. (1-based indexing)"`
@@ -16,13 +19,15 @@ type BusCmdGroup struct {
Eq BusEqCmdGroup ` help:"Commands related to the bus EQ." cmd:"eq"` Eq BusEqCmdGroup ` help:"Commands related to the bus EQ." cmd:"eq"`
Comp BusCompCmdGroup ` help:"Commands related to the bus compressor." cmd:"comp"` Comp BusCompCmdGroup ` help:"Commands related to the bus compressor." cmd:"comp"`
} `arg:"" help:"The index of the bus."` } `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 { 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"` 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 { func (cmd *BusMuteCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.State == nil { if cmd.State == nil {
resp, err := ctx.Client.Bus.Mute(bus.Index.Index) resp, err := ctx.Client.Bus.Mute(bus.Index.Index)
@@ -40,10 +45,12 @@ func (cmd *BusMuteCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusFaderCmd defines the command for getting or setting the fader level of a bus.
type BusFaderCmd struct { type BusFaderCmd struct {
Level *float64 `arg:"" help:"The fader level to set (in dB). If not provided, the current fader level will be returned."` 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 { func (cmd *BusFaderCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Level == nil { if cmd.Level == nil {
resp, err := ctx.Client.Bus.Fader(bus.Index.Index) resp, err := ctx.Client.Bus.Fader(bus.Index.Index)
@@ -61,11 +68,13 @@ func (cmd *BusFaderCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusFadeinCmd defines the command for fading in a bus over a specified duration to a target fader level.
type BusFadeinCmd struct { type BusFadeinCmd struct {
Duration time.Duration `flag:"" help:"The duration of the fade-in effect." default:"5s"` 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:""` 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 { func (cmd *BusFadeinCmd) Run(ctx *context, bus *BusCmdGroup) error {
currentLevel, err := ctx.Client.Bus.Fader(bus.Index.Index) currentLevel, err := ctx.Client.Bus.Fader(bus.Index.Index)
if err != nil { if err != nil {
@@ -98,11 +107,13 @@ func (cmd *BusFadeinCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusFadeoutCmd defines the command for fading out a bus over a specified duration to a target fader level.
type BusFadeoutCmd struct { type BusFadeoutCmd struct {
Duration time.Duration `flag:"" help:"The duration of the fade-out effect." default:"5s"` 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:""` 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 { func (cmd *BusFadeoutCmd) Run(ctx *context, bus *BusCmdGroup) error {
currentLevel, err := ctx.Client.Bus.Fader(bus.Index.Index) currentLevel, err := ctx.Client.Bus.Fader(bus.Index.Index)
if err != nil { if err != nil {
@@ -135,10 +146,12 @@ func (cmd *BusFadeoutCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusNameCmd defines the command for getting or setting the name of a bus.
type BusNameCmd struct { type BusNameCmd struct {
Name *string `arg:"" help:"The name to set for the bus. If not provided, the current name will be returned."` 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 { func (cmd *BusNameCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Name == nil { if cmd.Name == nil {
resp, err := ctx.Client.Bus.Name(bus.Index.Index) resp, err := ctx.Client.Bus.Name(bus.Index.Index)
@@ -156,29 +169,33 @@ func (cmd *BusNameCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusEqCmdGroup defines the commands related to controlling the EQ of a bus.
type BusEqCmdGroup struct { type BusEqCmdGroup struct {
On BusEqOnCmd `help:"Get or set the EQ on/off state of the bus." cmd:"on"` 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 (graphic or parametric)." cmd:"mode"` Mode BusEqModeCmd `help:"Get or set the EQ mode of the bus (peq, geq or teq)." cmd:"mode"`
Band struct { Band struct {
Band int `arg:"" help:"The EQ band number."` Band int `arg:"" help:"The EQ band number."`
Gain BusEqBandGainCmd `help:"Get or set the gain of the EQ band." cmd:"gain"` 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"` 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"` 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 (bell, high shelf, low shelf, high pass, low pass)." cmd:"type"` 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:""` } `help:"Commands for controlling a specific EQ band of the bus." arg:""`
} }
func (cmd *BusEqCmdGroup) Validate() error { // 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 { if cmd.Band.Band < 1 || cmd.Band.Band > 6 {
return fmt.Errorf("EQ band number must be between 1 and 6") return fmt.Errorf("EQ band number must be between 1 and 6")
} }
return nil return nil
} }
// BusCompCmdGroup defines the commands related to controlling the compressor of a bus.
type BusEqOnCmd struct { 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"` 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 { func (cmd *BusEqOnCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.State == nil { if cmd.State == nil {
resp, err := ctx.Client.Bus.Eq.On(bus.Index.Index) resp, err := ctx.Client.Bus.Eq.On(bus.Index.Index)
@@ -196,10 +213,12 @@ func (cmd *BusEqOnCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusEqModeCmd defines the command for getting or setting the EQ mode of a bus.
type BusEqModeCmd struct { type BusEqModeCmd struct {
Mode *string `arg:"" help:"The EQ mode to set (graphic or parametric). If not provided, the current EQ mode will be returned." optional:"" enum:"peq,geq,teq"` 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 { func (cmd *BusEqModeCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Mode == nil { if cmd.Mode == nil {
resp, err := ctx.Client.Bus.Eq.Mode(bus.Index.Index) resp, err := ctx.Client.Bus.Eq.Mode(bus.Index.Index)
@@ -217,10 +236,12 @@ func (cmd *BusEqModeCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusEqBandGainCmd defines the command for getting or setting the gain of a specific EQ band of a bus.
type BusEqBandGainCmd struct { 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."` 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 { func (cmd *BusEqBandGainCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmdGroup) error {
if cmd.Gain == nil { if cmd.Gain == nil {
resp, err := ctx.Client.Bus.Eq.Gain(bus.Index.Index, busEq.Band.Band) resp, err := ctx.Client.Bus.Eq.Gain(bus.Index.Index, busEq.Band.Band)
@@ -238,10 +259,12 @@ func (cmd *BusEqBandGainCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmd
return nil return nil
} }
// BusEqBandFreqCmd defines the command for getting or setting the frequency of a specific EQ band of a bus.
type BusEqBandFreqCmd struct { 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."` 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 { func (cmd *BusEqBandFreqCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmdGroup) error {
if cmd.Freq == nil { if cmd.Freq == nil {
resp, err := ctx.Client.Bus.Eq.Frequency(bus.Index.Index, busEq.Band.Band) resp, err := ctx.Client.Bus.Eq.Frequency(bus.Index.Index, busEq.Band.Band)
@@ -259,10 +282,12 @@ func (cmd *BusEqBandFreqCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmd
return nil return nil
} }
// BusEqBandQCmd defines the command for getting or setting the Q factor of a specific EQ band of a bus.
type BusEqBandQCmd struct { 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."` 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 { func (cmd *BusEqBandQCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmdGroup) error {
if cmd.Q == nil { if cmd.Q == nil {
resp, err := ctx.Client.Bus.Eq.Q(bus.Index.Index, busEq.Band.Band) resp, err := ctx.Client.Bus.Eq.Q(bus.Index.Index, busEq.Band.Band)
@@ -280,10 +305,12 @@ func (cmd *BusEqBandQCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmdGro
return nil return nil
} }
// BusEqBandTypeCmd defines the command for getting or setting the type of a specific EQ band of a bus.
type BusEqBandTypeCmd struct { type BusEqBandTypeCmd struct {
Type *string `arg:"" help:"The type to set for the EQ band (bell, high shelf, low shelf, high pass, low pass). If not provided, the current type will be returned." optional:"" enum:"lcut,lshv,peq,veq,hshv,hcut"` 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 { func (cmd *BusEqBandTypeCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmdGroup) error {
if cmd.Type == nil { if cmd.Type == nil {
resp, err := ctx.Client.Bus.Eq.Type(bus.Index.Index, busEq.Band.Band) resp, err := ctx.Client.Bus.Eq.Type(bus.Index.Index, busEq.Band.Band)
@@ -301,22 +328,25 @@ func (cmd *BusEqBandTypeCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmd
return nil return nil
} }
// BusCompCmdGroup defines the commands related to controlling the compressor of a bus.
type BusCompCmdGroup struct { type BusCompCmdGroup struct {
On BusCompOnCmd `help:"Get or set the compressor on/off state of the bus." cmd:"on"` 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 (standard, vintage, or modern)." cmd:"mode"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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 { 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"` 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 { func (cmd *BusCompOnCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.State == nil { if cmd.State == nil {
resp, err := ctx.Client.Bus.Comp.On(bus.Index.Index) resp, err := ctx.Client.Bus.Comp.On(bus.Index.Index)
@@ -334,10 +364,12 @@ func (cmd *BusCompOnCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusCompModeCmd defines the command for getting or setting the compressor mode of a bus.
type BusCompModeCmd struct { type BusCompModeCmd struct {
Mode *string `arg:"" help:"The compressor mode to set (standard, vintage, or modern). If not provided, the current compressor mode will be returned." optional:"" enum:"comp,exp"` 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 { func (cmd *BusCompModeCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Mode == nil { if cmd.Mode == nil {
resp, err := ctx.Client.Bus.Comp.Mode(bus.Index.Index) resp, err := ctx.Client.Bus.Comp.Mode(bus.Index.Index)
@@ -355,10 +387,12 @@ func (cmd *BusCompModeCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusCompThresholdCmd defines the command for getting or setting the compressor threshold of a bus.
type BusCompThresholdCmd struct { type BusCompThresholdCmd struct {
Threshold *float64 `arg:"" help:"The compressor threshold to set (in dB). If not provided, the current compressor threshold will be returned."` 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 { func (cmd *BusCompThresholdCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Threshold == nil { if cmd.Threshold == nil {
resp, err := ctx.Client.Bus.Comp.Threshold(bus.Index.Index) resp, err := ctx.Client.Bus.Comp.Threshold(bus.Index.Index)
@@ -376,10 +410,12 @@ func (cmd *BusCompThresholdCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusCompRatioCmd defines the command for getting or setting the compressor ratio of a bus.
type BusCompRatioCmd struct { type BusCompRatioCmd struct {
Ratio *float64 `arg:"" help:"The compressor ratio to set. If not provided, the current compressor ratio will be returned."` 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 { func (cmd *BusCompRatioCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Ratio == nil { if cmd.Ratio == nil {
resp, err := ctx.Client.Bus.Comp.Ratio(bus.Index.Index) resp, err := ctx.Client.Bus.Comp.Ratio(bus.Index.Index)
@@ -397,10 +433,12 @@ func (cmd *BusCompRatioCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusCompMixCmd defines the command for getting or setting the compressor mix level of a bus.
type BusCompMixCmd struct { 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."` 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 { func (cmd *BusCompMixCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Mix == nil { if cmd.Mix == nil {
resp, err := ctx.Client.Bus.Comp.Mix(bus.Index.Index) resp, err := ctx.Client.Bus.Comp.Mix(bus.Index.Index)
@@ -418,10 +456,12 @@ func (cmd *BusCompMixCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusCompMakeupCmd defines the command for getting or setting the compressor makeup gain of a bus.
type BusCompMakeupCmd struct { 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."` 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 { func (cmd *BusCompMakeupCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Makeup == nil { if cmd.Makeup == nil {
resp, err := ctx.Client.Bus.Comp.Makeup(bus.Index.Index) resp, err := ctx.Client.Bus.Comp.Makeup(bus.Index.Index)
@@ -439,10 +479,12 @@ func (cmd *BusCompMakeupCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusCompAttackCmd defines the command for getting or setting the compressor attack time of a bus.
type BusCompAttackCmd struct { 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."` 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 { func (cmd *BusCompAttackCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Attack == nil { if cmd.Attack == nil {
resp, err := ctx.Client.Bus.Comp.Attack(bus.Index.Index) resp, err := ctx.Client.Bus.Comp.Attack(bus.Index.Index)
@@ -460,10 +502,12 @@ func (cmd *BusCompAttackCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusCompHoldCmd defines the command for getting or setting the compressor hold time of a bus.
type BusCompHoldCmd struct { 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."` 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 { func (cmd *BusCompHoldCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Hold == nil { if cmd.Hold == nil {
resp, err := ctx.Client.Bus.Comp.Hold(bus.Index.Index) resp, err := ctx.Client.Bus.Comp.Hold(bus.Index.Index)
@@ -481,10 +525,12 @@ func (cmd *BusCompHoldCmd) Run(ctx *context, bus *BusCmdGroup) error {
return nil return nil
} }
// BusCompReleaseCmd defines the command for getting or setting the compressor release time of a bus.
type BusCompReleaseCmd struct { 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."` 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 { func (cmd *BusCompReleaseCmd) Run(ctx *context, bus *BusCmdGroup) error {
if cmd.Release == nil { if cmd.Release == nil {
resp, err := ctx.Client.Bus.Comp.Release(bus.Index.Index) resp, err := ctx.Client.Bus.Comp.Release(bus.Index.Index)

View File

@@ -7,6 +7,7 @@ import (
"github.com/charmbracelet/log" "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 { type HeadampCmdGroup struct {
Index struct { Index struct {
Index int `arg:"" help:"The index of the headamp."` Index int `arg:"" help:"The index of the headamp."`
@@ -15,11 +16,13 @@ type HeadampCmdGroup struct {
} `arg:"" help:"Control a specific headamp by index."` } `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 { type HeadampGainCmd struct {
Duration time.Duration `help:"The duration of the fade in/out when setting the gain." default:"5s"` 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:""` 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 { func (cmd *HeadampGainCmd) Run(ctx *context, headamp *HeadampCmdGroup) error {
if cmd.Gain == nil { if cmd.Gain == nil {
resp, err := ctx.Client.HeadAmp.Gain(headamp.Index.Index) resp, err := ctx.Client.HeadAmp.Gain(headamp.Index.Index)
@@ -88,10 +91,12 @@ func gradualGainAdjust(
return nil 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 { type HeadampPhantomCmd struct {
State *string `help:"The phantom power state of the headamp." arg:"" enum:"true,on,false,off" optional:""` 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 { func (cmd *HeadampPhantomCmd) Validate() error {
if cmd.State != nil { if cmd.State != nil {
switch *cmd.State { switch *cmd.State {
@@ -106,6 +111,7 @@ func (cmd *HeadampPhantomCmd) Validate() error {
return nil 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 { func (cmd *HeadampPhantomCmd) Run(ctx *context, headamp *HeadampCmdGroup) error {
if cmd.State == nil { if cmd.State == nil {
resp, err := ctx.Client.HeadAmp.PhantomPower(headamp.Index.Index) resp, err := ctx.Client.HeadAmp.PhantomPower(headamp.Index.Index)

View File

@@ -1,6 +1,7 @@
package xair package xair
var xairAddressMap = map[string]string{ var xairAddressMap = map[string]string{
"main": "/lr",
"strip": "/ch/%02d", "strip": "/ch/%02d",
"bus": "/bus/%01d", "bus": "/bus/%01d",
"headamp": "/headamp/%02d", "headamp": "/headamp/%02d",
@@ -8,9 +9,12 @@ var xairAddressMap = map[string]string{
} }
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 {

View File

@@ -26,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")
} }
@@ -52,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")
} }
@@ -75,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")
} }

View File

@@ -10,10 +10,6 @@ import (
"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
@@ -45,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(),
@@ -60,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
} }
@@ -88,32 +86,35 @@ func (c *Client) SendMessage(address string, args ...any) error {
} }
// ReceiveMessage receives an OSC message from the mixer // ReceiveMessage receives an OSC message from the mixer
func (c *Client) ReceiveMessage(timeout time.Duration) (*osc.Message, error) { func (c *Client) ReceiveMessage() (*osc.Message, error) {
t := time.Tick(timeout) t := time.Tick(c.engine.timeout)
select { select {
case <-t: case <-t:
return nil, nil return nil, fmt.Errorf("timeout waiting for response")
case val := <-c.respChan: case msg := <-c.respChan:
if val == nil { if msg == nil {
return nil, fmt.Errorf("no message received") return nil, fmt.Errorf("no message received")
} }
return val, nil return msg, nil
} }
} }
// RequestInfo requests mixer information // RequestInfo requests mixer information
func (c *Client) RequestInfo() (InfoResponse, error) { func (c *Client) RequestInfo() (InfoResponse, error) {
var info InfoResponse
err := c.SendMessage("/xinfo") err := c.SendMessage("/xinfo")
if err != nil { if err != nil {
return InfoResponse{}, err 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) if len(msg.Arguments) >= 3 {
info.Model = val.Arguments[2].(string) info.Host = msg.Arguments[0].(string)
info.Name = msg.Arguments[1].(string)
info.Model = msg.Arguments[2].(string)
} }
return info, nil return info, nil
} }

View File

@@ -31,8 +31,11 @@ func (c *Comp) On(index int) (bool, error) {
return false, err return false, err
} }
resp := <-c.client.respChan msg, err := c.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 Compressor on value") return false, fmt.Errorf("unexpected argument type for Compressor on value")
} }
@@ -59,8 +62,11 @@ func (c *Comp) Mode(index int) (string, error) {
possibleModes := []string{"comp", "exp"} possibleModes := []string{"comp", "exp"}
resp := <-c.client.respChan msg, err := c.client.ReceiveMessage()
val, ok := resp.Arguments[0].(int32) if err != nil {
return "", err
}
val, ok := msg.Arguments[0].(int32)
if !ok { if !ok {
return "", fmt.Errorf("unexpected argument type for Compressor mode value") return "", fmt.Errorf("unexpected argument type for Compressor mode value")
} }
@@ -82,8 +88,11 @@ func (c *Comp) Threshold(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-c.client.respChan msg, err := c.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 Compressor threshold value") return 0, fmt.Errorf("unexpected argument type for Compressor threshold value")
} }
@@ -106,8 +115,11 @@ func (c *Comp) Ratio(index int) (float32, error) {
possibleValues := []float32{1.1, 1.3, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 7.0, 10, 20, 100} possibleValues := []float32{1.1, 1.3, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 7.0, 10, 20, 100}
resp := <-c.client.respChan msg, err := c.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 Compressor ratio value") return 0, fmt.Errorf("unexpected argument type for Compressor ratio value")
} }
@@ -131,8 +143,11 @@ func (c *Comp) Attack(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-c.client.respChan msg, err := c.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 Compressor attack value") return 0, fmt.Errorf("unexpected argument type for Compressor attack value")
} }
@@ -153,8 +168,11 @@ func (c *Comp) Hold(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-c.client.respChan msg, err := c.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 Compressor hold value") return 0, fmt.Errorf("unexpected argument type for Compressor hold value")
} }
@@ -175,8 +193,11 @@ func (c *Comp) Release(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-c.client.respChan msg, err := c.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 Compressor release value") return 0, fmt.Errorf("unexpected argument type for Compressor release value")
} }
@@ -197,8 +218,11 @@ func (c *Comp) Makeup(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-c.client.respChan msg, err := c.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 Compressor makeup gain value") return 0, fmt.Errorf("unexpected argument type for Compressor makeup gain value")
} }
@@ -219,8 +243,11 @@ func (c *Comp) Mix(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-c.client.respChan msg, err := c.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 Compressor mix value") return 0, fmt.Errorf("unexpected argument type for Compressor mix value")
} }

View File

@@ -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 {

View File

@@ -31,8 +31,11 @@ func (e *Eq) On(index int) (bool, error) {
return false, err return false, err
} }
resp := <-e.client.respChan msg, err := e.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 EQ on value") return false, fmt.Errorf("unexpected argument type for EQ on value")
} }
@@ -58,8 +61,11 @@ func (e *Eq) Mode(index int) (string, error) {
possibleModes := []string{"peq", "geq", "teq"} possibleModes := []string{"peq", "geq", "teq"}
resp := <-e.client.respChan msg, err := e.client.ReceiveMessage()
val, ok := resp.Arguments[0].(int32) if err != nil {
return "", err
}
val, ok := msg.Arguments[0].(int32)
if !ok { if !ok {
return "", fmt.Errorf("unexpected argument type for EQ mode value") return "", fmt.Errorf("unexpected argument type for EQ mode value")
} }
@@ -80,8 +86,11 @@ func (e *Eq) Gain(index int, band int) (float64, error) {
return 0, err return 0, err
} }
resp := <-e.client.respChan msg, err := e.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 EQ gain value") return 0, fmt.Errorf("unexpected argument type for EQ gain value")
} }
@@ -102,8 +111,11 @@ func (e *Eq) Frequency(index int, band int) (float64, error) {
return 0, err return 0, err
} }
resp := <-e.client.respChan msg, err := e.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 EQ frequency value") return 0, fmt.Errorf("unexpected argument type for EQ frequency value")
} }
@@ -124,8 +136,11 @@ func (e *Eq) Q(index int, band int) (float64, error) {
return 0, err return 0, err
} }
resp := <-e.client.respChan msg, err := e.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 EQ Q value") return 0, fmt.Errorf("unexpected argument type for EQ Q value")
} }
@@ -148,8 +163,11 @@ func (e *Eq) Type(index int, band int) (string, error) {
possibleTypes := []string{"lcut", "lshv", "peq", "veq", "hshv", "hcut"} possibleTypes := []string{"lcut", "lshv", "peq", "veq", "hshv", "hcut"}
resp := <-e.client.respChan msg, err := e.client.ReceiveMessage()
val, ok := resp.Arguments[0].(int32) if err != nil {
return "", err
}
val, ok := msg.Arguments[0].(int32)
if !ok { if !ok {
return "", fmt.Errorf("unexpected argument type for EQ type value") return "", fmt.Errorf("unexpected argument type for EQ type value")
} }

View File

@@ -19,8 +19,11 @@ func (g *Gate) On(index int) (bool, error) {
return false, err return false, err
} }
resp := <-g.client.respChan msg, err := g.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 Gate on value") return false, fmt.Errorf("unexpected argument type for Gate on value")
} }
@@ -47,8 +50,11 @@ func (g *Gate) Mode(index int) (string, error) {
possibleModes := []string{"exp2", "exp3", "exp4", "gate", "duck"} possibleModes := []string{"exp2", "exp3", "exp4", "gate", "duck"}
resp := <-g.client.respChan msg, err := g.client.ReceiveMessage()
val, ok := resp.Arguments[0].(int32) if err != nil {
return "", err
}
val, ok := msg.Arguments[0].(int32)
if !ok { if !ok {
return "", fmt.Errorf("unexpected argument type for Gate mode value") return "", fmt.Errorf("unexpected argument type for Gate mode value")
} }
@@ -71,8 +77,11 @@ func (g *Gate) Threshold(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-g.client.respChan msg, err := g.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 Gate threshold value") return 0, fmt.Errorf("unexpected argument type for Gate threshold value")
} }
@@ -93,8 +102,11 @@ func (g *Gate) Range(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-g.client.respChan msg, err := g.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 Gate range value") return 0, fmt.Errorf("unexpected argument type for Gate range value")
} }
@@ -115,8 +127,11 @@ func (g *Gate) Attack(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-g.client.respChan msg, err := g.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 Gate attack value") return 0, fmt.Errorf("unexpected argument type for Gate attack value")
} }
@@ -137,8 +152,11 @@ func (g *Gate) Hold(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-g.client.respChan msg, err := g.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 Gate hold value") return 0, fmt.Errorf("unexpected argument type for Gate hold value")
} }
@@ -159,8 +177,11 @@ func (g *Gate) Release(index int) (float64, error) {
return 0, err return 0, err
} }
resp := <-g.client.respChan msg, err := g.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 Gate release value") return 0, fmt.Errorf("unexpected argument type for Gate release value")
} }

View File

@@ -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")
} }

View File

@@ -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)
} }

View File

@@ -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
}
}

View File

@@ -16,14 +16,17 @@ func NewSnapshot(c *Client) *Snapshot {
// Name gets the name of the snapshot at the given index. // Name gets the name of the snapshot at the given index.
func (s *Snapshot) Name(index int) (string, error) { func (s *Snapshot) Name(index int) (string, error) {
address := s.baseAddress + fmt.Sprintf("/name/%d", index) address := s.baseAddress + fmt.Sprintf("/%02d/name", index)
err := s.client.SendMessage(address) err := s.client.SendMessage(address)
if err != nil { if err != nil {
return "", err return "", err
} }
resp := <-s.client.respChan msg, err := s.client.ReceiveMessage()
name, ok := resp.Arguments[0].(string) if err != nil {
return "", err
}
name, ok := msg.Arguments[0].(string)
if !ok { if !ok {
return "", fmt.Errorf("unexpected argument type for snapshot name") return "", fmt.Errorf("unexpected argument type for snapshot name")
} }
@@ -32,24 +35,30 @@ func (s *Snapshot) Name(index int) (string, error) {
// SetName sets the name of the snapshot at the given index. // SetName sets the name of the snapshot at the given index.
func (s *Snapshot) SetName(index int, name string) error { func (s *Snapshot) SetName(index int, name string) error {
address := s.baseAddress + fmt.Sprintf("/name/%d", index) address := s.baseAddress + fmt.Sprintf("/%02d/name", index)
return s.client.SendMessage(address, name) return s.client.SendMessage(address, name)
} }
// Load loads the snapshot at the given index. // CurrentName sets the name of the current snapshot.
func (s *Snapshot) Load(index int) error { func (s *Snapshot) CurrentName(name string) error {
address := s.baseAddress + fmt.Sprintf("/load/%d", index) address := s.baseAddress + "/name"
return s.client.SendMessage(address) return s.client.SendMessage(address, name)
} }
// Save saves the current state to the snapshot at the given index. // CurrentLoad loads the snapshot at the given index.
func (s *Snapshot) Save(index int) error { func (s *Snapshot) CurrentLoad(index int) error {
address := s.baseAddress + fmt.Sprintf("/save/%d", index) address := s.baseAddress + "/load"
return s.client.SendMessage(address) return s.client.SendMessage(address, int32(index))
} }
// Delete deletes the snapshot at the given index. // CurrentSave saves the current state to the snapshot at the given index.
func (s *Snapshot) Delete(index int) error { func (s *Snapshot) CurrentSave(index int) error {
address := s.baseAddress + fmt.Sprintf("/delete/%d", index) address := s.baseAddress + "/save"
return s.client.SendMessage(address) 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))
} }

View File

@@ -28,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")
} }
@@ -54,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")
} }
@@ -77,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")
} }
@@ -99,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")
} }
@@ -121,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")
} }

19
lr.go
View File

@@ -5,18 +5,21 @@ import (
"time" "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 { type MainCmdGroup struct {
Mute MainMuteCmd `help:"Get or set the mute state of the Main L/R output." cmd:""` 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:""` Fader MainFaderCmd `help:"Get or set the fader level of the Main L/R output." cmd:""`
Fadein MainFadeinCmd `help:"Get or set the fade-in time 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:"Get or set the fade-out time of the Main L/R output." 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 { type MainMuteCmd struct {
Mute *bool `arg:"" help:"The mute state to set. If not provided, the current state will be printed."` 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 { func (cmd *MainMuteCmd) Run(ctx *context) error {
if cmd.Mute == nil { if cmd.Mute == nil {
resp, err := ctx.Client.Main.Mute() resp, err := ctx.Client.Main.Mute()
@@ -34,10 +37,12 @@ func (cmd *MainMuteCmd) Run(ctx *context) error {
return nil 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 { type MainFaderCmd struct {
Level *float64 `arg:"" help:"The fader level to set. If not provided, the current level will be printed."` 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 { func (cmd *MainFaderCmd) Run(ctx *context) error {
if cmd.Level == nil { if cmd.Level == nil {
resp, err := ctx.Client.Main.Fader() resp, err := ctx.Client.Main.Fader()
@@ -55,11 +60,13 @@ func (cmd *MainFaderCmd) Run(ctx *context) error {
return nil 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 { type MainFadeinCmd struct {
Duration time.Duration `flag:"" help:"The duration of the fade-in. (in seconds.)" default:"5s"` 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:""` 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 { func (cmd *MainFadeinCmd) Run(ctx *context) error {
currentLevel, err := ctx.Client.Main.Fader() currentLevel, err := ctx.Client.Main.Fader()
if err != nil { if err != nil {
@@ -87,11 +94,13 @@ func (cmd *MainFadeinCmd) Run(ctx *context) error {
return nil 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 { type MainFadeoutCmd struct {
Duration time.Duration `flag:"" help:"The duration of the fade-out. (in seconds.)" default:"5s"` 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:""` 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 { func (cmd *MainFadeoutCmd) Run(ctx *context) error {
currentLevel, err := ctx.Client.Main.Fader() currentLevel, err := ctx.Client.Main.Fader()
if err != nil { if err != nil {

38
main.go
View File

@@ -6,6 +6,7 @@ import (
"os" "os"
"runtime/debug" "runtime/debug"
"strings" "strings"
"time"
"github.com/alecthomas/kong" "github.com/alecthomas/kong"
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
@@ -32,23 +33,28 @@ type context struct {
} }
type Config struct { type Config struct {
Host string `default:"mixer.local" help:"The host of the X-Air device." env:"XAIR_CLI_HOST"` 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"` Port int `default:"10024" help:"The port of the X-Air device." env:"XAIR_CLI_PORT" short:"P"`
Kind string `default:"xr18" help:"The kind of the X-Air device." env:"XAIR_CLI_KIND"` 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 { type CLI struct {
Config `embed:"" prefix:"" help:"The configuration for the CLI."` Config `embed:"" prefix:"" help:"The configuration for the CLI."`
Version VersionFlag `help:"Print gobs-cli version information and quit" name:"version" short:"v"` 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"` Completion kongcompletion.Completion `help:"Generate shell completion scripts." cmd:"" aliases:"c"`
Raw RawCmdGroup `help:"Send raw OSC messages to the mixer." cmd:"" group:"Raw"` 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"` Main MainCmdGroup `help:"Control the Main L/R output" cmd:"" group:"Main"`
Strip StripCmdGroup `help:"Control the strips." cmd:"" group:"Strip"` Strip StripCmdGroup `help:"Control the strips." cmd:"" group:"Strip"`
Bus BusCmdGroup `help:"Control the buses." cmd:"" group:"Bus"` Bus BusCmdGroup `help:"Control the buses." cmd:"" group:"Bus"`
Headamp HeadampCmdGroup `help:"Control input gain and phantom power." cmd:"" group:"Headamp"` 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() {
@@ -82,6 +88,12 @@ func main() {
// run is the main entry point for the CLI. // run is the main entry point for the CLI.
// It connects to the X-Air device, retrieves mixer info, and then runs the command. // It connects to the X-Air device, retrieves mixer info, and then runs the command.
func run(ctx *kong.Context, config Config) error { 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) client, err := connect(config)
if err != nil { if err != nil {
return fmt.Errorf("failed to connect to X-Air device: %w", err) return fmt.Errorf("failed to connect to X-Air device: %w", err)
@@ -103,8 +115,14 @@ func run(ctx *kong.Context, config Config) error {
return ctx.Run() return ctx.Run()
} }
// connect creates a new X-Air client based on the provided configuration.
func connect(config Config) (*xair.Client, error) { func connect(config Config) (*xair.Client, error) {
client, err := xair.NewClient(config.Host, config.Port, xair.WithKind(config.Kind)) client, err := xair.NewClient(
config.Host,
config.Port,
xair.WithKind(config.Kind),
xair.WithTimeout(config.Timeout),
)
if err != nil { if err != nil {
return nil, err return nil, err
} }

29
raw.go
View File

@@ -2,20 +2,18 @@ package main
import ( import (
"fmt" "fmt"
"time"
"github.com/charmbracelet/log"
) )
type RawCmdGroup struct { // RawCmd represents the command to send raw OSC messages to the mixer.
Send RawSendCmd `help:"Send a raw OSC message to the mixer." cmd:""` 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:""`
} }
type RawSendCmd struct { // Run executes the RawCmd by sending the specified OSC message to the mixer and optionally waiting for a response.
Timeout time.Duration `help:"Timeout for the OSC message send operation." default:"200ms" short:"t"` func (cmd *RawCmd) Run(ctx *context) error {
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:""`
}
func (cmd *RawSendCmd) Run(ctx *context) error {
params := make([]any, len(cmd.Args)) params := make([]any, len(cmd.Args))
for i, arg := range cmd.Args { for i, arg := range cmd.Args {
params[i] = arg params[i] = arg
@@ -24,11 +22,18 @@ func (cmd *RawSendCmd) Run(ctx *context) error {
return fmt.Errorf("failed to send raw OSC message: %w", err) return fmt.Errorf("failed to send raw OSC message: %w", err)
} }
msg, err := ctx.Client.ReceiveMessage(cmd.Timeout) 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 { if err != nil {
return fmt.Errorf("failed to receive response for raw OSC message: %w", err) return fmt.Errorf("failed to receive response for raw OSC message: %w", err)
} }
fmt.Fprintf(ctx.Out, "Received response: %s with args: %v\n", msg.Address, msg.Arguments) if msg != nil {
fmt.Fprintf(ctx.Out, "Received response: %s with args: %v\n", msg.Address, msg.Arguments)
}
return nil return nil
} }

75
snapshot.go Normal file
View 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 to a snapshot." cmd:"save"`
Load LoadCmd `help:"Load a mixer state from a snapshot." cmd:"load"`
Delete DeleteCmd `help:"Delete a snapshot." cmd:"delete"`
} `help:"The index of the snapshot." arg:""`
}
type ListCmd struct {
}
func (c *ListCmd) Run(ctx *context) error {
for i := range 64 {
name, err := ctx.Client.Snapshot.Name(i + 1)
if err != nil {
break
}
if name == "" {
continue
}
fmt.Fprintf(ctx.Out, "%d: %s\n", i+1, name)
}
return nil
}
type NameCmd struct {
Name *string `arg:"" help:"The name of the snapshot." optional:""`
}
func (c *NameCmd) Run(ctx *context, snapshot *SnapshotCmdGroup) error {
if c.Name == nil {
name, err := ctx.Client.Snapshot.Name(snapshot.Index.Index)
if err != nil {
return err
}
fmt.Fprintln(ctx.Out, name)
return nil
}
return ctx.Client.Snapshot.SetName(snapshot.Index.Index, *c.Name)
}
type SaveCmd struct {
Name string `arg:"" help:"The name of the snapshot."`
}
func (c *SaveCmd) Run(ctx *context, snapshot *SnapshotCmdGroup) error {
err := ctx.Client.Snapshot.CurrentName(c.Name)
if err != nil {
return err
}
return ctx.Client.Snapshot.CurrentSave(snapshot.Index.Index)
}
type LoadCmd struct {
}
func (c *LoadCmd) Run(ctx *context, snapshot *SnapshotCmdGroup) error {
return ctx.Client.Snapshot.CurrentLoad(snapshot.Index.Index)
}
type DeleteCmd struct {
}
func (c *DeleteCmd) Run(ctx *context, snapshot *SnapshotCmdGroup) error {
return ctx.Client.Snapshot.CurrentDelete(snapshot.Index.Index)
}

View File

@@ -3,8 +3,11 @@ package main
import ( import (
"fmt" "fmt"
"time" "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 { type StripCmdGroup struct {
Index struct { Index struct {
Index int `arg:"" help:"The index of the strip. (1-based indexing)"` Index int `arg:"" help:"The index of the strip. (1-based indexing)"`
@@ -18,13 +21,15 @@ type StripCmdGroup struct {
Gate StripGateCmdGroup ` help:"Commands related to the strip gate." cmd:"gate"` Gate StripGateCmdGroup ` help:"Commands related to the strip gate." cmd:"gate"`
Eq StripEqCmdGroup ` help:"Commands related to the strip EQ." cmd:"eq"` Eq StripEqCmdGroup ` help:"Commands related to the strip EQ." cmd:"eq"`
Comp StripCompCmdGroup ` help:"Commands related to the strip compressor." cmd:"comp"` Comp StripCompCmdGroup ` help:"Commands related to the strip compressor." cmd:"comp"`
} `arg:"" help:"The index of the strip."` } `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 { 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"` 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 { func (cmd *StripMuteCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.State == nil { if cmd.State == nil {
resp, err := ctx.Client.Strip.Mute(strip.Index.Index) resp, err := ctx.Client.Strip.Mute(strip.Index.Index)
@@ -42,10 +47,12 @@ func (cmd *StripMuteCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil return nil
} }
// StripFaderCmd defines the command for getting or setting the fader level of a strip.
type StripFaderCmd struct { type StripFaderCmd struct {
Level *float64 `arg:"" help:"The fader level to set (in dB)." optional:""` 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 { func (cmd *StripFaderCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Level == nil { if cmd.Level == nil {
resp, err := ctx.Client.Strip.Fader(strip.Index.Index) resp, err := ctx.Client.Strip.Fader(strip.Index.Index)
@@ -63,11 +70,13 @@ func (cmd *StripFaderCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripFadeinCmd struct {
Duration time.Duration `flag:"" help:"The duration of the fade-in (in seconds)." default:"5s"` 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:""` 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 { func (cmd *StripFadeinCmd) Run(ctx *context, strip *StripCmdGroup) error {
currentLevel, err := ctx.Client.Strip.Fader(strip.Index.Index) currentLevel, err := ctx.Client.Strip.Fader(strip.Index.Index)
if err != nil { if err != nil {
@@ -96,11 +105,13 @@ func (cmd *StripFadeinCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripFadeoutCmd struct {
Duration time.Duration `flag:"" help:"The duration of the fade-out (in seconds)." default:"5s"` 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:""` 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 { func (cmd *StripFadeoutCmd) Run(ctx *context, strip *StripCmdGroup) error {
{ {
currentLevel, err := ctx.Client.Strip.Fader(strip.Index.Index) currentLevel, err := ctx.Client.Strip.Fader(strip.Index.Index)
@@ -131,11 +142,13 @@ func (cmd *StripFadeoutCmd) Run(ctx *context, strip *StripCmdGroup) error {
} }
} }
// 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 { type StripSendCmd struct {
BusNum int `arg:"" help:"The bus number to get or set the send level for."` BusNum int `arg:"" help:"The bus number to get or set the send level for."`
Level *string `arg:"" help:"The send level to set (in dB)." optional:""` 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 { func (cmd *StripSendCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Level == nil { if cmd.Level == nil {
resp, err := ctx.Client.Strip.SendLevel(strip.Index.Index, cmd.BusNum) resp, err := ctx.Client.Strip.SendLevel(strip.Index.Index, cmd.BusNum)
@@ -146,18 +159,19 @@ func (cmd *StripSendCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil return nil
} }
level := mustConvToFloat64(*cmd.Level) if err := ctx.Client.Strip.SetSendLevel(strip.Index.Index, cmd.BusNum, *cmd.Level); err != nil {
if err := ctx.Client.Strip.SetSendLevel(strip.Index.Index, cmd.BusNum, level); err != nil {
return fmt.Errorf("failed to set send level: %w", err) 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, level) 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 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 { type StripNameCmd struct {
Name *string `arg:"" help:"The name to set for the strip." optional:""` 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 { func (cmd *StripNameCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Name == nil { if cmd.Name == nil {
resp, err := ctx.Client.Strip.Name(strip.Index.Index) resp, err := ctx.Client.Strip.Name(strip.Index.Index)
@@ -175,20 +189,23 @@ func (cmd *StripNameCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripGateCmdGroup struct {
On StripGateOnCmd `help:"Get or set the gate on/off state of the strip." cmd:""` 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:""` 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:""` Threshold StripGateThresholdCmd `help:"Get or set the gate threshold of the strip." cmd:""`
Range StripGateRangeCmd `help:"Get the gate range 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:""` 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:""` 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:""` 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 { type StripGateOnCmd struct {
Enable *string `arg:"" help:"Whether to enable or disable the gate." optional:"" enum:"true,false"` 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 { func (cmd *StripGateOnCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Enable == nil { if cmd.Enable == nil {
resp, err := ctx.Client.Strip.Gate.On(strip.Index.Index) resp, err := ctx.Client.Strip.Gate.On(strip.Index.Index)
@@ -206,10 +223,12 @@ func (cmd *StripGateOnCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripGateModeCmd struct {
Mode *string `arg:"" help:"The gate mode to set." optional:"" enum:"exp2,exp3,exp4,gate,duck"` 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 { func (cmd *StripGateModeCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Mode == nil { if cmd.Mode == nil {
resp, err := ctx.Client.Strip.Gate.Mode(strip.Index.Index) resp, err := ctx.Client.Strip.Gate.Mode(strip.Index.Index)
@@ -227,10 +246,12 @@ func (cmd *StripGateModeCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripGateThresholdCmd struct {
Threshold *float64 `arg:"" help:"The gate threshold to set (in dB)." optional:""` 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 { func (cmd *StripGateThresholdCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Threshold == nil { if cmd.Threshold == nil {
resp, err := ctx.Client.Strip.Gate.Threshold(strip.Index.Index) resp, err := ctx.Client.Strip.Gate.Threshold(strip.Index.Index)
@@ -248,10 +269,12 @@ func (cmd *StripGateThresholdCmd) Run(ctx *context, strip *StripCmdGroup) error
return nil 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 { type StripGateRangeCmd struct {
Range *float64 `arg:"" help:"The gate range to set (in dB)." optional:""` 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 { func (cmd *StripGateRangeCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Range == nil { if cmd.Range == nil {
resp, err := ctx.Client.Strip.Gate.Range(strip.Index.Index) resp, err := ctx.Client.Strip.Gate.Range(strip.Index.Index)
@@ -269,10 +292,12 @@ func (cmd *StripGateRangeCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripGateAttackCmd struct {
Attack *float64 `arg:"" help:"The gate attack time to set (in ms)." optional:""` 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 { func (cmd *StripGateAttackCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Attack == nil { if cmd.Attack == nil {
resp, err := ctx.Client.Strip.Gate.Attack(strip.Index.Index) resp, err := ctx.Client.Strip.Gate.Attack(strip.Index.Index)
@@ -290,10 +315,12 @@ func (cmd *StripGateAttackCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripGateHoldCmd struct {
Hold *float64 `arg:"" help:"The gate hold time to set (in ms)." optional:""` 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 { func (cmd *StripGateHoldCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Hold == nil { if cmd.Hold == nil {
resp, err := ctx.Client.Strip.Gate.Hold(strip.Index.Index) resp, err := ctx.Client.Strip.Gate.Hold(strip.Index.Index)
@@ -311,10 +338,12 @@ func (cmd *StripGateHoldCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripGateReleaseCmd struct {
Release *float64 `arg:"" help:"The gate release time to set (in ms)." optional:""` 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 { func (cmd *StripGateReleaseCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Release == nil { if cmd.Release == nil {
resp, err := ctx.Client.Strip.Gate.Release(strip.Index.Index) resp, err := ctx.Client.Strip.Gate.Release(strip.Index.Index)
@@ -332,6 +361,7 @@ func (cmd *StripGateReleaseCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripEqCmdGroup struct {
On StripEqOnCmd `help:"Get or set the EQ on/off state of the strip." cmd:""` On StripEqOnCmd `help:"Get or set the EQ on/off state of the strip." cmd:""`
Band struct { Band struct {
@@ -343,17 +373,20 @@ type StripEqCmdGroup struct {
} `help:"Commands for controlling a specific EQ band of the strip." arg:""` } `help:"Commands for controlling a specific EQ band of the strip." arg:""`
} }
func (cmd *StripEqCmdGroup) Validate() error { // 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 { if cmd.Band.Band < 1 || cmd.Band.Band > 4 {
return fmt.Errorf("EQ band number must be between 1 and 4") return fmt.Errorf("EQ band number must be between 1 and 4")
} }
return nil 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 { type StripEqOnCmd struct {
Enable *string `arg:"" help:"Whether to enable or disable the EQ." optional:"" enum:"true,false"` 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 { func (cmd *StripEqOnCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Enable == nil { if cmd.Enable == nil {
resp, err := ctx.Client.Strip.Eq.On(strip.Index.Index) resp, err := ctx.Client.Strip.Eq.On(strip.Index.Index)
@@ -371,10 +404,12 @@ func (cmd *StripEqOnCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripEqBandGainCmd struct {
Gain *float64 `arg:"" help:"The gain to set for the EQ band (in dB)." optional:""` 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 { func (cmd *StripEqBandGainCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *StripEqCmdGroup) error {
if cmd.Gain == nil { if cmd.Gain == nil {
resp, err := ctx.Client.Strip.Eq.Gain(strip.Index.Index, stripEq.Band.Band) resp, err := ctx.Client.Strip.Eq.Gain(strip.Index.Index, stripEq.Band.Band)
@@ -392,10 +427,12 @@ func (cmd *StripEqBandGainCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *
return nil 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 { type StripEqBandFreqCmd struct {
Freq *float64 `arg:"" help:"The frequency to set for the EQ band (in Hz)." optional:""` 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 { func (cmd *StripEqBandFreqCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *StripEqCmdGroup) error {
if cmd.Freq == nil { if cmd.Freq == nil {
resp, err := ctx.Client.Strip.Eq.Frequency(strip.Index.Index, stripEq.Band.Band) resp, err := ctx.Client.Strip.Eq.Frequency(strip.Index.Index, stripEq.Band.Band)
@@ -419,10 +456,12 @@ func (cmd *StripEqBandFreqCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *
return nil 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 { type StripEqBandQCmd struct {
Q *float64 `arg:"" help:"The Q factor to set for the EQ band." optional:""` 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 { func (cmd *StripEqBandQCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *StripEqCmdGroup) error {
if cmd.Q == nil { if cmd.Q == nil {
resp, err := ctx.Client.Strip.Eq.Q(strip.Index.Index, stripEq.Band.Band) resp, err := ctx.Client.Strip.Eq.Q(strip.Index.Index, stripEq.Band.Band)
@@ -440,10 +479,12 @@ func (cmd *StripEqBandQCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *Str
return nil 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 StripEqBandTypeCmd struct {
Type *string `arg:"" help:"The type to set for the EQ band." optional:"" enum:"lcut,lshv,peq,veq,hshv,hcut"` 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 { func (cmd *StripEqBandTypeCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *StripEqCmdGroup) error {
if cmd.Type == nil { if cmd.Type == nil {
resp, err := ctx.Client.Strip.Eq.Type(strip.Index.Index, stripEq.Band.Band) resp, err := ctx.Client.Strip.Eq.Type(strip.Index.Index, stripEq.Band.Band)
@@ -461,6 +502,7 @@ func (cmd *StripEqBandTypeCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *
return nil 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 { type StripCompCmdGroup struct {
On StripCompOnCmd `help:"Get or set the compressor on/off state of the strip." cmd:""` 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:""` Mode StripCompModeCmd `help:"Get or set the compressor mode of the strip." cmd:""`
@@ -473,10 +515,12 @@ type StripCompCmdGroup struct {
Release StripCompReleaseCmd `help:"Get or set the compressor release 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 { type StripCompOnCmd struct {
Enable *string `arg:"" help:"Whether to enable or disable the compressor." optional:"" enum:"true,false"` 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 { func (cmd *StripCompOnCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Enable == nil { if cmd.Enable == nil {
resp, err := ctx.Client.Strip.Comp.On(strip.Index.Index) resp, err := ctx.Client.Strip.Comp.On(strip.Index.Index)
@@ -494,10 +538,12 @@ func (cmd *StripCompOnCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripCompModeCmd struct {
Mode *string `arg:"" help:"The compressor mode to set." optional:"" enum:"comp,exp"` 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 { func (cmd *StripCompModeCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Mode == nil { if cmd.Mode == nil {
resp, err := ctx.Client.Strip.Comp.Mode(strip.Index.Index) resp, err := ctx.Client.Strip.Comp.Mode(strip.Index.Index)
@@ -515,10 +561,12 @@ func (cmd *StripCompModeCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripCompThresholdCmd struct {
Threshold *float64 `arg:"" help:"The compressor threshold to set (in dB)." optional:""` 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 { func (cmd *StripCompThresholdCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Threshold == nil { if cmd.Threshold == nil {
resp, err := ctx.Client.Strip.Comp.Threshold(strip.Index.Index) resp, err := ctx.Client.Strip.Comp.Threshold(strip.Index.Index)
@@ -536,10 +584,12 @@ func (cmd *StripCompThresholdCmd) Run(ctx *context, strip *StripCmdGroup) error
return nil 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 { type StripCompRatioCmd struct {
Ratio *float64 `arg:"" help:"The compressor ratio to set." optional:""` 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 { func (cmd *StripCompRatioCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Ratio == nil { if cmd.Ratio == nil {
resp, err := ctx.Client.Strip.Comp.Ratio(strip.Index.Index) resp, err := ctx.Client.Strip.Comp.Ratio(strip.Index.Index)
@@ -557,10 +607,12 @@ func (cmd *StripCompRatioCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripCompMixCmd struct {
Mix *float64 `arg:"" help:"The compressor mix to set (0-100%)." optional:""` 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 { func (cmd *StripCompMixCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Mix == nil { if cmd.Mix == nil {
resp, err := ctx.Client.Strip.Comp.Mix(strip.Index.Index) resp, err := ctx.Client.Strip.Comp.Mix(strip.Index.Index)
@@ -578,10 +630,12 @@ func (cmd *StripCompMixCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripCompMakeupCmd struct {
Makeup *float64 `arg:"" help:"The compressor makeup gain to set (in dB)." optional:""` 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 { func (cmd *StripCompMakeupCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Makeup == nil { if cmd.Makeup == nil {
resp, err := ctx.Client.Strip.Comp.Makeup(strip.Index.Index) resp, err := ctx.Client.Strip.Comp.Makeup(strip.Index.Index)
@@ -599,10 +653,12 @@ func (cmd *StripCompMakeupCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripCompAttackCmd struct {
Attack *float64 `arg:"" help:"The compressor attack time to set (in ms)." optional:""` 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 { func (cmd *StripCompAttackCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Attack == nil { if cmd.Attack == nil {
resp, err := ctx.Client.Strip.Comp.Attack(strip.Index.Index) resp, err := ctx.Client.Strip.Comp.Attack(strip.Index.Index)
@@ -620,10 +676,12 @@ func (cmd *StripCompAttackCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripCompHoldCmd struct {
Hold *float64 `arg:"" help:"The compressor hold time to set (in ms)." optional:""` 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 { func (cmd *StripCompHoldCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Hold == nil { if cmd.Hold == nil {
resp, err := ctx.Client.Strip.Comp.Hold(strip.Index.Index) resp, err := ctx.Client.Strip.Comp.Hold(strip.Index.Index)
@@ -641,10 +699,12 @@ func (cmd *StripCompHoldCmd) Run(ctx *context, strip *StripCmdGroup) error {
return nil 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 { type StripCompReleaseCmd struct {
Release *float64 `arg:"" help:"The compressor release time to set (in ms)." optional:""` 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 { func (cmd *StripCompReleaseCmd) Run(ctx *context, strip *StripCmdGroup) error {
if cmd.Release == nil { if cmd.Release == nil {
resp, err := ctx.Client.Strip.Comp.Release(strip.Index.Index) resp, err := ctx.Client.Strip.Comp.Release(strip.Index.Index)

12
util.go
View File

@@ -1,12 +0,0 @@
package main
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
}