Overview
IDMXGenerator is the core interface for implementing custom DMX data generation logic. Generators can create, modify, or transform DMX channel values in real-time, enabling effects like fades, remapping, animations, and complex lighting sequences.
When to Implement
Implement IDMXGenerator when you need to:
- Generate DMX data procedurally (e.g., animations, effects)
- Modify existing DMX channel values (e.g., fades, intensity scaling)
- Remap or redirect DMX channels
- Apply time-based effects to lighting
- Integrate external data sources (e.g., audio, OSC, timecode)
- Implement custom lighting behaviors and patterns
Interface Definition
public interface IDMXGenerator : IUserInterface<IDMXGenerator>, IConstructable
{
void GenerateDMX(ref List<byte> dmxData);
}
Methods
GenerateDMX
The main method called each frame to generate or modify DMX channel data. This method receives the current DMX data and can read from it, modify existing values, or write completely new values.
The DMX channel data array. Each index represents a DMX channel (0-511 for standard DMX universe). Values range from 0-255. You can both read and write to this array.
This method does not return a value. All changes are made by modifying the dmxData parameter directly.
public void GenerateDMX(ref List<byte> dmxData)
{
// Read current channel values
byte currentValue = dmxData[0];
// Modify channel values
dmxData[0] = 255; // Set channel 0 to full
dmxData[1] = (byte)(currentValue / 2); // Set channel 1 to half of channel 0's value
}
Complete Implementation Examples
Example 1: Fade Generator
This generator smoothly fades specified channels to a target value over time:
using System;
using System.Collections.Generic;
using UnityEngine;
public class FadeGenerator : IDMXGenerator
{
public List<DMXChannel> channels = new List<DMXChannel>();
public EquationNumber valueToFadeTo = 0;
public TimeSpan fadeDuration = TimeSpan.FromSeconds(5);
private TimeSpan fadeStart;
private TimeSpan fadeEnd;
public void Construct() { }
public void Deconstruct() { }
public void GenerateDMX(ref List<byte> dmxData)
{
// Calculate fade progress (0 to 1)
var now = DateTime.Now.TimeOfDay;
float t = Mathf.InverseLerp(
(float)fadeStart.TotalMilliseconds,
(float)fadeEnd.TotalMilliseconds,
(float)now.TotalMilliseconds
);
// Apply fade to each specified channel
foreach (var channel in channels)
{
dmxData[channel] = (byte)Mathf.Lerp(dmxData[channel], valueToFadeTo, t);
}
}
public void ConstructUserInterface(RectTransform rect)
{
// Fade In button (fade from dark to bright)
Util.AddButton(rect, "Fade In")
.WithCallback(() =>
{
fadeStart = DateTime.Now.TimeOfDay + fadeDuration;
fadeEnd = DateTime.Now.TimeOfDay;
});
// Fade Out button (fade from bright to dark)
Util.AddButton(rect, "Fade Out")
.WithCallback(() =>
{
fadeStart = DateTime.Now.TimeOfDay;
fadeEnd = DateTime.Now.TimeOfDay + fadeDuration;
});
// Duration input
Util.AddInputField(rect, "Fade Duration (s)")
.WithText(fadeDuration.TotalSeconds.ToString())
.WithCallback((value) =>
{
if (double.TryParse(value, out double result))
{
fadeDuration = TimeSpan.FromSeconds(result);
}
});
}
public void DeconstructUserInterface() { }
public void UpdateUserInterface() { }
}
Example 2: Channel Mapper
This generator remaps one channel’s value to another:
using System.Collections.Generic;
using UnityEngine;
public class ChannelMapper : IDMXGenerator
{
public DMXChannel sourceChannel = 0;
public DMXChannel targetChannel = 1;
public float multiplier = 1.0f;
public void Construct() { }
public void Deconstruct() { }
public void GenerateDMX(ref List<byte> dmxData)
{
// Read from source channel
byte sourceValue = dmxData[sourceChannel];
// Apply multiplier and write to target
float scaled = sourceValue * multiplier;
dmxData[targetChannel] = (byte)Mathf.Clamp(scaled, 0, 255);
}
public void ConstructUserInterface(RectTransform rect)
{
Util.AddInputField(rect, "Source Channel")
.WithText(((int)sourceChannel).ToString())
.WithCallback((value) =>
{
if (int.TryParse(value, out int result))
{
sourceChannel = (DMXChannel)result;
}
});
Util.AddInputField(rect, "Target Channel")
.WithText(((int)targetChannel).ToString())
.WithCallback((value) =>
{
if (int.TryParse(value, out int result))
{
targetChannel = (DMXChannel)result;
}
});
Util.AddInputField(rect, "Multiplier")
.WithText(multiplier.ToString())
.WithCallback((value) =>
{
if (float.TryParse(value, out float result))
{
multiplier = result;
}
});
}
public void DeconstructUserInterface() { }
public void UpdateUserInterface() { }
}
Example 3: Wave Effect Generator
This generator creates a wave pattern across multiple channels:
using System;
using System.Collections.Generic;
using UnityEngine;
public class WaveGenerator : IDMXGenerator
{
public int startChannel = 0;
public int channelCount = 10;
public float speed = 1.0f;
public float amplitude = 255f;
public void Construct() { }
public void Deconstruct() { }
public void GenerateDMX(ref List<byte> dmxData)
{
float time = Time.time * speed;
for (int i = 0; i < channelCount; i++)
{
int channel = startChannel + i;
if (channel >= dmxData.Count) break;
// Create sine wave offset by channel index
float wave = Mathf.Sin(time + i * 0.5f);
// Map from [-1, 1] to [0, amplitude]
byte value = (byte)Mathf.Clamp((wave + 1f) * 0.5f * amplitude, 0, 255);
dmxData[channel] = value;
}
}
public void ConstructUserInterface(RectTransform rect)
{
Util.AddInputField(rect, "Start Channel")
.WithText(startChannel.ToString())
.WithCallback((value) =>
{
if (int.TryParse(value, out int result))
{
startChannel = result;
}
});
Util.AddInputField(rect, "Channel Count")
.WithText(channelCount.ToString())
.WithCallback((value) =>
{
if (int.TryParse(value, out int result))
{
channelCount = result;
}
});
Util.AddInputField(rect, "Speed")
.WithText(speed.ToString())
.WithCallback((value) =>
{
if (float.TryParse(value, out float result))
{
speed = result;
}
});
}
public void DeconstructUserInterface() { }
public void UpdateUserInterface() { }
}
Inherited Interfaces
IUserInterface<IDMXGenerator>
Generators can provide custom UI elements for configuration through methods like ConstructUserInterface(), DeconstructUserInterface(), and UpdateUserInterface().
IConstructable
Generators must implement lifecycle methods Construct() and Deconstruct() for resource management and initialization.
Best Practices
- Performance:
GenerateDMX() is called every frame. Minimize allocations and expensive calculations
- Channel Bounds: Always check that channel indices are within the valid range (0-511)
- Value Clamping: Ensure output values stay within 0-255 range
- State Management: Use
Construct() and Deconstruct() for initialization and cleanup
- Time-Based Effects: Use
Time.time or DateTime.Now for time-based animations
- Read Then Write: When modifying existing values, read the current value first to enable layering of multiple generators
Generator Pipeline
Multiple generators can be chained together. Each generator receives the output of the previous generator:
Initial DMX Data → Generator 1 → Generator 2 → Generator N → Final DMX Output
This allows you to compose complex effects from simple generators.
Common Use Cases
- Intensity Control: Scale all channels by a master intensity value
- Blackout: Set all channels to 0 when triggered
- Effects: Create chases, strobes, and animated patterns
- Remapping: Route channels to different outputs
- Audio Reactivity: Modulate channels based on audio input
- Timecode Sync: Trigger cues based on SMPTE timecode
- Procedural Animation: Generate complex lighting sequences algorithmically
See Also