Skip to content

Repository files navigation

Calrec CSCP Protocol Client

npm version License: MIT

A Node.js client library for communicating with Calrec audio consoles using the CSCP (Calrec Serial Control Protocol). This library provides a robust, type-safe interface for basic control and monitoring of Calrec mixing consoles over TCP/IP.

Features

  • 🔌 TCP/IP Communication - Connect to Calrec consoles over network
  • 🎛️ Basic Console Control - Control faders, mutes, routing, and more
  • 📡 Real-time Monitoring - Receive live updates from the console
  • 🔄 Auto-reconnection - Automatic reconnection on connection loss
  • 💓 Heartbeat - Notices a console that has gone away even when the socket stays open
  • 🛡️ Type Safety - Full TypeScript support with detailed type definitions
  • 🎯 Event-driven - Clean event-based API for state changes
  • 📊 Unit Conversion - Built-in dB/level conversion utilities
  • ⚙️ Configurable Timing - Runtime-configurable protocol timing settings
  • 🚀 Async/Await - Modern async API with robust error handling

What This Module Provides

This library implements a subset of the Calrec CSCP protocol, providing control over:

  • Fader Control: Level, mute (cut), PFL, and labels
  • Main Fader Control: Level, PFL, and labels for main outputs
  • Auxiliary Routing: Route channels to auxiliary outputs (V20+ consoles)
  • Main Routing: Route channels to main outputs (V21+ consoles)
  • Stereo Image Control: Configure stereo image settings (V21+ consoles)
  • Console Information: Get console details and protocol version

Note: This is not a complete console control solution. Calrec consoles offer many more features and capabilities that are not covered by this protocol implementation. This library provides a solid foundation for basic control and monitoring tasks.

Installation

npm install @bitfocusas/calrec-cscp

Quick Start

import { CalrecClient } from '@bitfocusas/calrec-cscp';

// Create a client instance with custom settings
const client = new CalrecClient(
  {
    host: '192.168.1.100', // Your Calrec console IP
    port: 3322,            // Your configured TCP port
    maxFaderCount: 42,     // Maximum number of faders (1-192)
    maxMainCount: 3,       // Maximum number of main outputs (optional, default: 3)
    autoReconnect: true,
    reconnectInterval: 5000,
  },
  {
    // Optional: Custom protocol timing settings
    globalCommandRateMs: 10,      // Minimum ms between any command
    faderLevelRateMs: 100,        // Minimum ms between fader level commands
    commandResponseTimeoutMs: 20,  // Timeout for command responses
  }
);

// Set up event listeners
client.on('ready', async () => {
  console.log('Connected and ready!');
  
  // Example: Update settings at runtime
  client.updateSettings({
    faderLevelRateMs: 50, // Faster fader updates
  });
});

client.on('faderLevelChange', (faderId, level) => {
  console.log(`Fader ${faderId} level changed to ${level}`);
});

client.on('error', (error) => {
  console.error('Connection error:', error);
});

// Connect to the console (now async)
await client.connect();

Usage Examples

Basic Console Control

import { CalrecClient } from '@bitfocusas/calrec-cscp';

const client = new CalrecClient({
  host: '192.168.1.100',
  port: 3322,
  maxFaderCount: 42, // Maximum number of faders (1-192)
  maxMainCount: 3,   // Maximum number of main outputs (optional, default: 3)
});

client.on('ready', async () => {
  try {
    // Get console information (optional - console may not respond)
    try {
      const consoleInfo = await client.getConsoleInfo();
      console.log('Console:', consoleInfo.deskLabel);
      console.log('Max faders:', consoleInfo.maxFaders);
    } catch (error) {
      console.log('Console info not available, using configured values');
    }
    
    // Get configured fader count
    const maxFaders = client.getMaxFaderCount();
    console.log('Configured max faders:', maxFaders);
    
    // Set fader level (0-1023 range)
    await client.setFaderLevel(1, 500);
    
    // Set fader level in dB
    await client.setFaderLevelDb(1, -10);
    
    // Mute/unmute a fader
    await client.setFaderCut(1, true);
    
    // Get fader label
    const label = await client.getFaderLabel(1);
    console.log('Fader 1 label:', label);
  } catch (error) {
    console.error('Operation failed:', error);
  }
});

// Connect to the console
await client.connect();

Advanced Routing Control

client.on('ready', async () => {
  try {
    // Set aux routing (route fader 1 to aux 1 and 2)
    await client.setAuxRouting(1, [true, true, false, false]);
    
    // Set stereo image for a fader
    await client.setStereoImage(1, { leftToBoth: true, rightToBoth: false });
    
    // Get fader assignment
    const assignment = await client.getFaderAssignment(1);
    console.log('Fader 1 assignment:', assignment);
  } catch (error) {
    console.error('Routing operation failed:', error);
  }
});

// Monitor aux routing changes
client.on('auxRoutingChange', (auxId, routes) => {
  console.log(`Aux ${auxId} routing changed:`, routes);
});

Real-time Monitoring

// Monitor all fader changes
client.on('faderLevelChange', (faderId, level) => {
  console.log(`Fader ${faderId}: ${level}`);
});

client.on('faderCutChange', (faderId, isCut) => {
  console.log(`Fader ${faderId} ${isCut ? 'muted' : 'unmuted'}`);
});

client.on('faderLabelChange', (faderId, label) => {
  console.log(`Fader ${faderId} label: ${label}`);
});

client.on('faderAssignmentChange', (assignment) => {
  console.log('Fader assignment changed:', assignment);
});

client.on('stereoImageChange', (faderId, image) => {
  console.log(`Fader ${faderId} stereo image:`, image);
});

Connection Management

// Monitor connection state
client.on('connectionStateChange', (state) => {
  console.log('Connection state:', state);
});

client.on('connect', () => {
  console.log('Connected to console');
});

client.on('disconnect', () => {
  console.log('Disconnected from console');
});

// Manual disconnect (now async)
await client.disconnect();

Detecting a Lost Console

A network outage does not close a TCP socket, so a client that only waits for socket events keeps reporting connected long after the console is gone. Once the link goes quiet the client probes it with a console info read; after heartbeatMaxMisses unanswered probes it emits error, then disconnect, and reconnects if autoReconnect is enabled. Worst-case detection time is heartbeatIntervalMs * (heartbeatMaxMisses + 1) — 15 seconds by default.

Reconnect attempts are bounded by connectTimeoutMs, because a host that is off the network never answers a SYN and the OS would otherwise spend well over a minute retrying a single attempt.

const client = new CalrecClient(
  { host: '192.168.1.100', port: 3322, maxFaderCount: 42 },
  {
    heartbeatIntervalMs: 5000, // 0 disables heartbeats
    heartbeatMaxMisses: 2,
  }
);

client.on('error', (error) => {
  // "Console stopped responding: 2 heartbeat probe(s) unanswered, ..."
  console.error(error.message);
});

Error Handling

// Error handling
client.on('error', (error) => {
  console.error('Client error:', error.message);
});

// Handle errors in async operations
try {
  await client.setFaderLevel(1, 500);
} catch (error) {
  if (error.message.includes('NAK')) {
    console.error('Protocol error - command rejected by console');
  } else if (error.message.includes('timeout')) {
    console.error('Command timed out');
  } else if (error.message.includes('not connected')) {
    console.error('Client is not connected');
  } else {
    console.error('Unexpected error:', error);
  }
}

API Reference

CalrecClient

The main client class for communicating with Calrec consoles. This client provides access to a subset of the console's capabilities through the CSCP protocol.

Constructor

new CalrecClient(options: CalrecClientOptions, settings?: CalrecClientSettings)

Options:

  • host: string - Console IP address
  • port: number - TCP port number
  • autoReconnect?: boolean - Enable auto-reconnection (default: true)
  • reconnectInterval?: number - Reconnection delay in ms (default: 5000)
  • maxFaderCount?: number - Maximum number of faders (for validation)

Settings:

  • globalCommandRateMs?: number - Minimum ms between any command (default: 10)
  • faderLevelRateMs?: number - Minimum ms between fader level commands (default: 100)
  • commandResponseTimeoutMs?: number - Timeout for command responses (default: 500)
  • initializationTimeoutMs?: number - Timeout for console info/name during connect (default: 200)
  • connectTimeoutMs?: number - How long a connection attempt may take (default: 5000)
  • heartbeatIntervalMs?: number - How often an idle connection is probed (default: 5000, 0 disables)
  • heartbeatMaxMisses?: number - Unanswered probes tolerated before the connection is treated as lost (default: 2)

Methods

Connection Management
  • connect(): Promise<void> - Connect to the console
  • disconnect(): Promise<void> - Disconnect from the console
  • getState(): ClientState - Get current client state
  • getConnectionState(): ConnectionState - Get connection state
  • updateSettings(settings: CalrecClientSettings): void - Update protocol settings at runtime
Console Information
  • getConsoleInfo(): Promise<ConsoleInfo> - Get console information
  • getConsoleName(): Promise<string> - Get console name
Fader Control
  • setFaderLevel(faderId: number, level: number): Promise<void> - Set fader level (0-1023)
  • getFaderLevel(faderId: number): Promise<number> - Get fader level
  • setFaderLevelDb(faderId: number, db: number): Promise<void> - Set fader level in dB
  • getFaderLevelDb(faderId: number): Promise<number> - Get fader level in dB
  • setFaderCut(faderId: number, isCut: boolean): Promise<void> - Mute/unmute fader
  • getFaderCut(faderId: number): Promise<boolean> - Get fader cut state
  • getFaderLabel(faderId: number): Promise<string> - Get fader label
  • setFaderPfl(faderId: number, isPfl: boolean): Promise<void> - Set fader PFL state
  • getFaderPfl(faderId: number): Promise<boolean> - Get fader PFL state
  • getFaderAssignment(faderId: number): Promise<FaderAssignment> - Get fader assignment
Main Fader Control
  • setMainFaderLevelDb(mainId: number, db: number): Promise<void> - Set main fader level in dB
  • getMainFaderLevelDb(mainId: number): Promise<number> - Get main fader level in dB
  • setMainFaderPfl(mainId: number, isPfl: boolean): Promise<void> - Set main fader PFL state
  • getMainPfl(mainId: number): Promise<boolean> - Get main fader PFL state
  • getMainFaderLabel(mainId: number): Promise<string> - Get main fader label
Routing Control (V20+)
  • getAvailableAux(): Promise<boolean[]> - Get available auxiliary outputs
  • setAuxRouting(auxId: number, routes: boolean[]): Promise<void> - Set aux routing
  • getAuxSendRouting(auxId: number): Promise<boolean[]> - Get aux routing
  • setAuxOutputLevel(auxId: number, level: number): Promise<void> - Set aux output level
  • getAuxOutputLevel(auxId: number): Promise<number> - Get aux output level
Main Routing Control (V21+)
  • getAvailableMains(): Promise<boolean[]> - Get available main outputs
  • setRouteToMain(mainId: number, routes: boolean[]): Promise<void> - Set main routing
Stereo Image Control (V21+)
  • setStereoImage(faderId: number, image: StereoImage): Promise<void> - Set stereo image
  • getStereoImage(faderId: number): Promise<StereoImage> - Get stereo image

Events

The client extends EventEmitter and provides the following events:

Connection Events
  • connect - Connected to console
  • disconnect - Disconnected from console
  • ready - Client fully initialized and ready
  • error - Connection or protocol error
  • connectionStateChange - Connection state changed
Fader Events
  • faderLevelChange - Fader level changed
  • faderCutChange - Fader mute state changed
  • faderPflChange - Fader PFL state changed
  • faderLabelChange - Fader label changed
  • faderAssignmentChange - Fader assignment changed
Main Fader Events
  • mainLevelChange - Main fader level changed
  • mainPflChange - Main fader PFL state changed
  • mainLabelChange - Main fader label changed
Aux Events
  • availableAuxesChange - Available auxes changed
  • auxRoutingChange - Aux routing changed
  • auxOutputLevelChange - Aux output level changed
Main Routing Events
  • availableMainsChange - Available mains changed
  • mainRoutingChange - Main routing changed
Stereo Image Events
  • stereoImageChange - Stereo image settings changed
Other Events
  • unsolicitedMessage - Raw unsolicited message received (only for truly unknown commands)

Protocol levels

The snippets above follow the protocol level a console supports:

  • V1: Basic commands (all consoles)
  • V20: V1 + Auxiliary send routing extensions
  • V21: V20 + Channel/Group routing to mains extensions

Types

The library exports detailed TypeScript types:

import {
  ConnectionState,
  CalrecClientOptions,
  CalrecClientSettings,
  ConsoleInfo,
  FaderAssignment,
  AudioType,
  AudioWidth,
  StereoImage,
  NakError,
  ClientState,
  ParsedMessage,
  CalrecClientEvents
} from '@bitfocusas/calrec-cscp';

Converters

Utility functions for converting between different units:

import {
  dbToChannelLevel,
  dbToMainLevel,
  channelLevelToDb,
  mainLevelToDb
} from '@bitfocusas/calrec-cscp';

Configuration

Console Setup

  1. Configure your Calrec console to enable CSCP over TCP/IP
  2. Set the appropriate IP address and port
  3. Ensure network connectivity between your application and the console

Protocol Timing Settings

The library allows you to configure protocol timing for optimal performance:

const client = new CalrecClient(
  { host: '192.168.1.100', port: 3322 },
  {
    globalCommandRateMs: 10,      // Minimum ms between any command
    faderLevelRateMs: 100,        // Minimum ms between fader level commands
    commandResponseTimeoutMs: 20,  // Timeout for command responses
  }
);

// Update settings at runtime
client.updateSettings({
  faderLevelRateMs: 50, // Faster fader updates
});

Network Requirements

  • TCP/IP connectivity to the console
  • Port access (typically 3322, but configurable)
  • Stable network connection for reliable operation

Error Handling

The library provides detailed error handling with informative error messages:

client.on('error', (error) => {
  console.error('Client error:', error.message);
});

// Handle specific error types
try {
  await client.setFaderLevel(1, 500);
} catch (error) {
  if (error.message.includes('NAK')) {
    console.error('Protocol error - command rejected by console');
  } else if (error.message.includes('timeout')) {
    console.error('Command timed out');
  } else if (error.message.includes('not connected')) {
    console.error('Client is not connected');
  } else {
    console.error('Unexpected error:', error);
  }
}

Development

This repository contains TypeScript source only. dist/ is build output and is not committed. npm pack and npm publish compile it automatically before the package is packed.

Building from Source

git clone https://github.com/bitfocus/node-calrec-cscp.git
cd node-calrec-cscp
npm install
npm run build
npm test

Code Quality

npm run lint    # Lint code
npm run format  # Format code
npm run check   # Run all checks

Testing

The library includes both unit tests and integration tests. Integration tests require a physical Calrec console for full testing.

Contributing

We welcome contributions! This library is provided free of charge by Bitfocus AS to the audio community as a useful tool for basic Calrec console control.

How to Contribute

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests if applicable
  5. Run the linter and formatter (npm run check)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

Development Guidelines

  • Follow the existing code style (enforced by Biome)
  • Add TypeScript types for all new features
  • Include JSDoc comments for public APIs
  • Test your changes thoroughly
  • Update documentation as needed

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Note: This is a community project. While we strive to help with issues, support is provided on a best-effort basis.

Acknowledgments

  • Calrec Audio for the CSCP protocol specification
  • All contributors who help improve this library
  • The audio community for feedback and testing

Made with ❤️ by Bitfocus AS

About

Node module written in TypeScript for Calrec Serial Control Protocol

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages