mixr-js
v0.9.7
Published
A comprehensive TypeScript/JavaScript client library for controlling digital mixing consoles over network. Currently supports Behringer X-Air series (XR12, XR16, XR18) via OSC protocol, with extensible architecture designed for future mixer support across
Maintainers
Readme
Mixr.js
A comprehensive TypeScript/JavaScript client library for controlling digital mixing consoles over network. Currently supports Behringer X-Air series (XR12, XR16, XR18) via OSC protocol, with extensible architecture designed for future mixer support across different protocols.
Why "Mixr.js"? The name reflects the broader vision of a type-safe, modern mixing console control library. While currently focused on X-Air mixers, the architecture is designed to support multiple mixer brands and models.
✨ Features
🎛️ Complete Channel Control
- Channel Configuration: Name, color, input source routing
- Preamp: Gain, phantom power, low-cut filter, polarity invert
- 4-Band Parametric EQ: Frequency, gain, Q, band type control
- Dynamics: Gate and compressor with full parameter control
- Mix Controls: Fader, pan, mute, LR assignment
- Sends: Aux bus and FX sends with level and tap control
- Automix: Weight and group assignment
- Insert Effects: FX slot assignment
🎚️ Main LR Bus Control
- Mix Controls: Master fader, mute, pan control
- Configuration: Name and color assignment
- 6-Band Parametric EQ: Full frequency control with mode selection (PEQ/GEQ/TEQ)
- 31-Band Graphic EQ: Professional graphic equalizer with individual band control
- Dynamics: Professional compressor processing
- Insert Effects: FX slot assignment and processing
- Audio Processing: Complete signal path control for main outputs
🚌 Auxiliary Bus Control (Bus 1-6)
- Mix Controls: Bus level, mute control
- Configuration: Bus name and color assignment
- 6-Band Parametric EQ: Complete frequency shaping per bus
- 31-Band Graphic EQ: Professional graphic equalizer control per bus
- Dynamics: Full compressor processing per bus
- Group Assignments: DCA and mute group integration
- Insert Effects: Per-bus insert processing
🔄 Dual API Design
- Raw Protocol Values: Direct control using underlying protocol values for power users
- Audio Engineer Units: Decibels, Hertz, percentages for intuitive control
- Automatic Conversion: Seamless translation between raw and engineer-friendly values
🛡️ Type Safety
- Branded Types: Prevent unit confusion with compile-time safety
- Model-Specific Channels: TypeScript enforces valid channels per mixer model
- Comprehensive Validation: Runtime parameter validation with clear error messages
🔍 Network Discovery
- Auto-Discovery: Find mixers on your network automatically
- Manual Connection: Direct IP connection support
- Connection Management: Automatic connection handling with cleanup
📦 Installation
npm install mixr-js🚀 Quick Start
Discover and Connect to Mixers
import { discoverMixers, connectToMixer } from 'mixr-js';
// Auto-discover mixers on the network
const mixers = await discoverMixers({ timeout: 5000 });
console.log('Found mixers:', mixers);
// Connect to the first discovered XR18 mixer
const mixer = await connectToMixer({
...mixers[0],
model: 'XR18', // Specify your mixer model for type safety
});
// Always close connection when done
await mixer.closeConnection();Basic Channel Control
// Get a channel (TypeScript ensures valid channel names per model)
const channel = mixer.getChannel('CH01');
// Configure channel
await channel.getConfig().updateName('Lead Vocal');
await channel.getConfig().updateColor('Red', 'color');
// Preamp control - dual API in action
await channel.getPreAmp().updateGain(24, 'decibels'); // Audio engineer units
await channel.getPreAmp().updateGain(0.5); // Raw protocol value
// Enable phantom power and configure low-cut
await channel.getPreAmp().updatePhantomPowerEnabled(true, 'flag');
await channel.getPreAmp().updateLowCutFrequency(100, 'hertz');
// Mix controls
await channel.getMix().updateFader(-6, 'decibels');
await channel.getMix().updatePan(25, 'percent');
await channel.getMix().updateMuted(false, 'flag');EQ Control
const eq = channel.getEqualizer();
// Enable EQ
await eq.updateEnabled(true, 'flag');
// Configure high-mid band (band 3)
const highMid = eq.getBand(3);
await highMid.updateFrequency(3000, 'hertz');
await highMid.updateGain(2.5, 'decibels');
await highMid.updateQ(1.4, 'number');
await highMid.updateType('PEQ', 'type');Dynamics Processing
const compressor = channel.getCompressor();
// Configure compressor
await compressor.updateEnabled(true, 'flag');
await compressor.updateThreshold(-12, 'decibels');
await compressor.updateRatio('4', 'ratio');
await compressor.updateAttack(10, 'milliseconds');
await compressor.updateRelease(100, 'milliseconds');
await compressor.updateGain(3, 'decibels'); // Makeup gain
// Gate configuration
const gate = channel.getGate();
await gate.updateEnabled(true, 'flag');
await gate.updateThreshold(-35, 'decibels');
await gate.updateRange(40, 'decibels');Sends and Routing
// Aux send to Bus 1
const bus1Send = channel.getSendBus('Bus1');
await bus1Send.updateLevel(-10, 'decibels');
await bus1Send.updateTap('POST', 'tap');
// FX send
const fx1Send = channel.getSendFx('FX1');
await fx1Send.updateLevel(-15, 'decibels');
await fx1Send.updateTap('PRE', 'tap');
// DCA and Mute group assignment
await channel.getDCAGroup().updateEnabled(1);
await channel.getMuteGroup().updateEnabled(2);Main LR Bus Control
// Access the main LR bus
const mainLR = mixer.getMainLR();
// Mix controls
await mainLR.getMix().updateFader(-2, 'decibels');
await mainLR.getMix().updateMuted(false, 'flag');
await mainLR.getMix().updatePan(0, 'percent'); // Center pan
// Configuration
await mainLR.getConfig().updateName('Main Mix');
await mainLR.getConfig().updateColor('White', 'color');
// 6-band EQ with mode control
const lrEQ = mainLR.getEqualizer();
await lrEQ.updateEnabled(true, 'flag');
await lrEQ.updateMode('PEQ', 'mode'); // PEQ, GEQ, or TEQ
// Configure EQ bands (1-6)
const midBand = lrEQ.getBand(3);
await midBand.updateFrequency(1000, 'hertz');
await midBand.updateGain(1.5, 'decibels');
await midBand.updateQ(2.0, 'number');
// Compressor with insert support
const lrCompressor = mainLR.getCompressor();
await lrCompressor.updateEnabled(true, 'flag');
await lrCompressor.updateThreshold(-6, 'decibels');
await lrCompressor.updateRatio('3', 'ratio');
// 31-band graphic EQ control
const lrGEQ = mainLR.getGraphicEqualizer();
await lrGEQ.updateBandGain('1k', 2.5, 'decibels'); // Boost 1kHz by 2.5dB
await lrGEQ.updateBandGain('10k', -1.5, 'decibels'); // Cut 10kHz by 1.5dB
await lrGEQ.updateBandGain('100', 0.8); // Raw OSC value for 100Hz band
// Read current graphic EQ settings
const gain1k = await lrGEQ.fetchBandGain('1k', 'decibels');
const rawGain100 = await lrGEQ.fetchBandGain('100');
// Insert effects support
const lrInsert = mainLR.getInsert();
await lrInsert.updateEnabled(true, 'flag');
await lrInsert.updateFxSlot('FX1', 'slot');Auxiliary Bus Control
// Access an auxiliary bus (Bus 1-6)
const bus1 = mixer.getBus('Bus1');
// Mix controls
await bus1.getMix().updateFader(-8, 'decibels');
await bus1.getMix().updateMuted(false, 'flag');
// Configuration
await bus1.getConfig().updateName('Monitor 1');
await bus1.getConfig().updateColor('Blue', 'color');
// 6-band EQ control
const busEQ = bus1.getEqualizer();
await busEQ.updateEnabled(true, 'flag');
await busEQ.updateMode('PEQ', 'mode'); // PEQ, GEQ, TEQ, VEQ, LCut, LShv, HShv, HCut
// Configure EQ bands for monitor mix
const lowBand = busEQ.getBand(1);
await lowBand.updateFrequency(80, 'hertz');
await lowBand.updateGain(-2, 'decibels');
await lowBand.updateType('HPF', 'type');
const midBand = busEQ.getBand(3);
await midBand.updateFrequency(2500, 'hertz');
await midBand.updateGain(1, 'decibels');
await midBand.updateQ(1.5, 'number');
// Compressor for bus dynamics
const busCompressor = bus1.getCompressor();
await busCompressor.updateEnabled(true, 'flag');
await busCompressor.updateThreshold(-12, 'decibels');
await busCompressor.updateRatio('2', 'ratio');
// 31-band graphic EQ control
const busGEQ = bus1.getGraphicEqualizer();
await busGEQ.updateBandGain('500', 1.2, 'decibels'); // Boost 500Hz by 1.2dB
await busGEQ.updateBandGain('4k', -0.8, 'decibels'); // Cut 4kHz by 0.8dB
await busGEQ.updateBandGain('8k', 0.6); // Raw OSC value for 8kHz band
// Read current graphic EQ settings
const gain500 = await busGEQ.fetchBandGain('500', 'decibels');
const rawGain4k = await busGEQ.fetchBandGain('4k');
// Group assignments
await bus1.getDCAGroup().updateEnabled(3);
await bus1.getMuteGroup().updateEnabled(1);
// Insert effects
const busInsert = bus1.getInsert();
await busInsert.updateEnabled(true, 'flag');
await busInsert.updateFxSlot('FX2A', 'slot');Reading Current Values
// Read channel values with automatic unit conversion
const currentGain = await channel.getPreAmp().fetchGain('decibels');
const currentFreq = await eq.getBand(1).fetchFrequency('hertz');
const isMuted = await channel.getMix().fetchIsMuted('flag');
// Read main LR values
const mainLR = mixer.getMainLR();
const masterLevel = await mainLR.getMix().fetchFader('decibels');
const eqMode = await mainLR.getEqualizer().fetchMode('mode');
const compressorRatio = await mainLR.getCompressor().fetchRatio('ratio');
console.log(`Channel gain: ${currentGain}dB`);
console.log(`Low band frequency: ${currentFreq}Hz`);
console.log(`Channel muted: ${isMuted}`);
console.log(`Master level: ${masterLevel}dB`);
console.log(`Main EQ mode: ${eqMode}`);
console.log(`Main compressor ratio: ${compressorRatio}:1`);📚 API Reference
Mixer Models
| Model | Channels | Description |
| ------ | --------- | ------------------------------- |
| XR12 | CH01-CH10 | Behringer X-Air XR12 (12-input) |
| XR16 | CH01-CH14 | Behringer X-Air XR16 (16-input) |
| XR18 | CH01-CH16 | Behringer X-Air XR18 (18-input) |
Future versions will support additional mixer brands and models.
Note: This library has been extensively tested with real hardware on an XR18 mixer. XR12 and XR16 support is implemented based on protocol documentation and should work correctly, but has not been verified with physical hardware.
Channel Features
Configuration (channel.getConfig())
updateName(name)/fetchName()- Channel nameupdateColor(color)/fetchColor()- Color assignmentupdateAnalogSource(source)/fetchAnalogSource()- Analog input routingupdateUsbReturnSource(source)/fetchUsbReturnSource()- USB return source
Preamp (channel.getPreAmp())
updateGain(gain, 'decibels')/fetchGain()- Input gain (-12dB to +60dB)updatePhantomPowerEnabled(enabled)/fetchIsPhantomPowerEnabled()- 48V phantom powerupdateLowCutFrequency(freq, 'hertz')/fetchLowCutFrequency()- High-pass filter (20Hz-400Hz)updateLowCutEnabled(enabled)/fetchIsLowCutEnabled()- High-pass filter on/offupdatePolarityInverted(inverted)/fetchIsPolarityInverted()- Phase invertupdateUSBTrim(trim, 'decibels')/fetchUSBTrim()- USB return trim (-18dB to +18dB)updateUSBReturnEnabled(enabled)/fetchIsUSBReturnEnabled()- USB return on/off
4-Band EQ (channel.getEqualizer())
updateEnabled(enabled)/fetchIsEnabled()- EQ on/offgetBand(1-4)- Access individual EQ bandsupdateFrequency(freq, 'hertz')/fetchFrequency()- Center frequencyupdateGain(gain, 'decibels')/fetchGain()- Gain adjustment (-15dB to +15dB)updateQ(q, 'number')/fetchQ()- Q factor (0.3 to 10)updateType(type)/fetchType()- Band type (LCut, LShv, PEQ, HShv, HCut)
Dynamics (channel.getCompressor(), channel.getGate())
- Compressor: Threshold, ratio, attack, release, knee, makeup gain, mix
- Gate: Threshold, range, attack, hold, release, key source
- Shared: Enable/disable, key source, side-chain filter
Mix (channel.getMix())
updateFader(level, 'decibels')/fetchFader()- Channel fader (-∞ to +10dB)updatePan(pan, 'percent')/fetchPan()- Pan position (-100% to +100%)updateMuted(muted)/fetchIsMuted()- Channel muteupdateLeftRightAssignmentEnabled(enabled)/fetchIsLeftRightAssignmentEnabled()- LR assignment
Sends
getSendBus('Bus1'-'Bus6')- Aux bus sendsgetSendFx('FX1'-'FX4')- Effects sends- Level, tap point (PRE/POST), and group enable control
Groups and Routing
getDCAGroup()/getMuteGroup()- DCA and mute group assignmentsupdateEnabled(groupNumber)- Assign to groupupdateDisabled(groupNumber)- Remove from groupisEnabled(groupNumber)- Check group assignment
getInsert()- Insert effect slot assignmentupdateEnabled(enabled)/fetchIsEnabled()- Insert on/offupdateFxSlot(slot)/fetchFxSlot()- FX slot assignment
getAutomix()- Automix group and weight
Main LR Bus Features
Configuration (mixer.getMainLR().getConfig())
updateName(name)/fetchName()- Main LR nameupdateColor(color)/fetchColor()- Color assignment
Mix (mixer.getMainLR().getMix())
updateFader(level, 'decibels')/fetchFader()- Main fader (-∞ to +10dB)updateMuted(muted)/fetchIsMuted()- Main LR muteupdatePan(pan, 'percent')/fetchPan()- Pan position (-100% to +100%)
6-Band EQ (mixer.getMainLR().getEqualizer())
updateEnabled(enabled)/fetchIsEnabled()- EQ on/offupdateMode(mode)/fetchMode()- EQ mode (PEQ, GEQ, TEQ)getBand(1-6)- Access individual EQ bands- Full parametric control (frequency, gain, Q, type)
- Same interface as channel EQ bands
31-Band Graphic EQ (mixer.getMainLR().getGraphicEqualizer())
updateBandGain(band, gain, 'decibels')/fetchBandGain(band, 'decibels')- Band gain in dB (-15dB to +15dB)updateBandGain(band, gain)/fetchBandGain(band)- Band gain as raw OSC value (0.0-1.0)- Available Bands: '20', '25', '31.5', '40', '50', '63', '80', '100', '125', '160', '200', '250', '315', '400', '500', '630', '800', '1k', '1k25', '1k6', '2k', '2k5', '3k15', '4k', '5k', '6k3', '8k', '10k', '12k5', '16k', '20k'
Compressor (mixer.getMainLR().getCompressor())
- Full dynamics control (threshold, ratio, attack, release, etc.)
Insert Effects (mixer.getMainLR().getInsert())
The main LR bus supports insert effects with simplified slot assignments:
const insert = mixer.getMainLR().getInsert();
// Get current insert enabled state
const isEnabled = await insert.fetchIsEnabled('flag');
// Enable/disable insert
await insert.updateEnabled(true, 'flag');
// Get current FX slot assignment
const currentSlot = await insert.fetchFxSlot('slot');
// Set FX slot assignment
await insert.updateFxSlot('FX2', 'slot');Available FX Slots:
'OFF'- Insert disabled'FX1'- FX1 slot'FX2'- FX2 slot'FX3'- FX3 slot'FX4'- FX4 slot
Note: MainLR insert uses simplified slot names ('FX1', 'FX2', etc.) unlike channel inserts which use A/B variants ('Fx1A', 'Fx1B', etc.)
Auxiliary Bus Features (Bus 1-6)
Configuration (mixer.getBus('Bus1').getConfig())
updateName(name)/fetchName()- Bus nameupdateColor(color)/fetchColor()- Color assignment
Mix (mixer.getBus('Bus1').getMix())
updateFader(level, 'decibels')/fetchFader()- Bus fader (-∞ to +10dB)updateMuted(muted)/fetchIsMuted()- Bus mute
6-Band EQ (mixer.getBus('Bus1').getEqualizer())
updateEnabled(enabled)/fetchIsEnabled()- EQ on/offupdateMode(mode)/fetchMode()- EQ mode (LCut, LShv, PEQ, VEQ, HShv, HCut)getBand(1-6)- Access individual EQ bandsupdateFrequency(freq, 'hertz')/fetchFrequency()- Center frequencyupdateGain(gain, 'decibels')/fetchGain()- Gain adjustment (-15dB to +15dB)updateQ(q, 'number')/fetchQ()- Q factor (0.3 to 10)updateType(type)/fetchType()- Band type (LCut, LShv, PEQ, HShv, HCut)
31-Band Graphic EQ (mixer.getBus('Bus1').getGraphicEqualizer())
updateBandGain(band, gain, 'decibels')/fetchBandGain(band, 'decibels')- Band gain in dB (-15dB to +15dB)updateBandGain(band, gain)/fetchBandGain(band)- Band gain as raw OSC value (0.0-1.0)- Available Bands: '20', '25', '31.5', '40', '50', '63', '80', '100', '125', '160', '200', '250', '315', '400', '500', '630', '800', '1k', '1k25', '1k6', '2k', '2k5', '3k15', '4k', '5k', '6k3', '8k', '10k', '12k5', '16k', '20k'
Compressor (mixer.getBus('Bus1').getCompressor())
- Full dynamics control (threshold, ratio, attack, release, etc.)
- Same interface as channel compressor
Groups and Routing
getDCAGroup()/getMuteGroup()- DCA and mute group assignmentsupdateEnabled(groupNumber)- Assign to groupupdateDisabled(groupNumber)- Remove from groupisEnabled(groupNumber)- Check group assignment
getInsert()- Insert effect slot assignmentupdateEnabled(enabled)/fetchIsEnabled()- Insert on/offupdateFxSlot(slot)/fetchFxSlot()- FX slot assignment
🏗️ Architecture Highlights
Type System
The library uses a sophisticated branded type system to prevent unit confusion:
// Compile-time prevention of unit errors
await channel.getPreAmp().updateGain(24, 'decibels'); // ✅ Correct
await channel.getPreAmp().updateGain(24, 'hertz'); // ❌ TypeScript error
// Raw protocol values bypass unit system for power users
await channel.getPreAmp().updateGain(0.5); // ✅ Raw protocol valueModel-Specific Type Safety
const xr12 = await connectToMixer({ model: 'XR12', ...connection });
const xr18 = await connectToMixer({ model: 'XR18', ...connection });
xr12.getChannel('CH11'); // ❌ TypeScript error - XR12 only has CH01-CH10
xr18.getChannel('CH15'); // ✅ Valid - XR18 supports CH01-CH16Dual API Design
Every parameter supports both raw protocol values and audio engineer units:
// Audio engineer approach - intuitive units
await channel.getEqualizer().getBand(1).updateFrequency(1000, 'hertz');
await channel.getMix().updateFader(-6, 'decibels');
// Power user approach - direct protocol control
await channel.getEqualizer().getBand(1).updateFrequency(0.3);
await channel.getMix().updateFader(0.75);🧪 Testing
The library includes comprehensive test coverage (100%) with 315 tests:
npm test # Run all tests
npm run test:coverage # Run tests with coverage report
npm run test:coverage:html # Generate HTML coverage report🔧 Development
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run linting
npm run lint
# Format code
npm run format📋 Current Status & Limitations
This library currently implements comprehensive channel-level control, main LR bus control, and auxiliary bus control. Some mixer-wide features are not yet implemented:
- Effects rack control (FX1-FX4)
- System-level actions (snapshots, global mute)
- Advanced routing matrix
Recent Updates
Auxiliary Bus Implementation (Latest): Complete auxiliary bus control (Bus 1-6) including:
- Mix controls (fader, mute)
- Configuration (name, color)
- 6-band parametric EQ with full frequency control
- Compressor dynamics processing
- DCA and mute group assignments
- Insert effects routing
Main LR Bus Implementation: Complete main output control including:
- Mix controls (fader, mute, pan)
- 6-band parametric EQ with mode selection
- Professional compressor with insert effects
- Configuration (name, color)
See TODO.md for a complete list of planned features.
🤝 Contributing
Contributions are welcome! Please feel free to submit pull requests or open issues for:
- Bug fixes
- New feature implementations
- Documentation improvements
- Test coverage expansion
📄 License
MIT License - see LICENSE file for details.
🙏 Acknowledgments
- Behringer for creating the X-Air mixer series
- Open Sound Control Community for the OSC specification
Note: This library is not officially affiliated with Behringer. X-Air and associated trademarks are property of their respective owners.
