Overview
Channel Modifiers allow you to transform DMX values sent to specific fixture channels. This is useful for correcting non-linear dimming curves, inverting channel behavior, or creating custom response curves for individual fixtures.
Understanding Channel Modifiers
A Channel Modifier maps input DMX values to output DMX values:
// From channelmodifier.h:20-79
class ChannelModifier final
{
public:
enum Type {
SystemTemplate = 0 , // Built-in templates
UserTemplate = 1 // User-created templates
};
QString name () const ; // Modifier name
Type type () const ; // Template type
// Map of input → output DMX values
QList < QPair < uchar , uchar >> modifierMap () const ;
// Get modified output for given input
uchar getValue ( uchar dmxValue ) const ;
};
How It Works
Input Value : QLC+ generates a DMX value (0-255)
Modifier Applied : The modifier transforms the value
Output Value : The modified value is sent to the fixture
Input DMX: 128 → [Modifier] → Output DMX: 180
Use Cases
Dimmer Curve Correction
Some LED fixtures have non-linear dimming:
At low values, dimming is too abrupt
At high values, changes are barely noticeable
A modifier can create a custom curve to compensate.
Channel Inversion
Invert a channel’s response:
Input 0 → Output 255
Input 128 → Output 127
Input 255 → Output 0
Useful when:
A fixture is physically mounted upside-down
Pan/tilt needs to be reversed
You want opposite behavior without reprogramming
Fine-Tuning Color Matching
Adjust individual color channels to match fixtures from different manufacturers or batches.
Custom Response Curves
Create exponential, logarithmic, or S-curve responses for specific effects.
Creating a Channel Modifier
Modifier Map Structure
A modifier is defined by a list of control points:
// From channelmodifier.cpp:53-88
void ChannelModifier :: setModifierMap ( QList < QPair < uchar , uchar >> map )
{
m_map = map;
m_values . fill ( 0 , 256 );
QPair < uchar, uchar > lastDMXPair;
for ( int i = 0 ; i < m_map . count (); i ++ )
{
QPair < uchar, uchar > dmxPair = m_map . at (i);
m_values [ dmxPair . first ] = dmxPair . second ;
if (i != 0 )
{
// Calculate linear interpolation between points
float dmxInc = 0 ;
if ( dmxPair . first - lastDMXPair . first > 0 )
dmxInc = ( float )( dmxPair . second - lastDMXPair . second ) /
( float )( dmxPair . first - lastDMXPair . first );
// Fill intermediate values
float floatVal = lastDMXPair . second ;
for ( int p = lastDMXPair . first ; p < dmxPair . first ; p ++ )
{
m_values [p] = floatVal;
floatVal += dmxInc;
}
}
lastDMXPair = dmxPair;
}
}
Values between control points are automatically interpolated linearly. You only need to define key points in the curve.
Example: Simple Invert Modifier
Two control points create a full inversion:
Control Points:
Input 0 → Output 255
Input 255 → Output 0
Interpolated:
Input 0 → Output 255
Input 64 → Output 191
Input 128 → Output 127
Input 192 → Output 63
Input 255 → Output 0
Example: Dimmer Curve
Multiple points create a custom curve:
Control Points:
Input 0 → Output 0 (Off stays off)
Input 25 → Output 60 (Boost low values)
Input 128 → Output 140 (Slightly boost mid)
Input 255 → Output 255 (Full stays full)
This makes low-end dimming smoother while preserving full brightness.
Applying Modifiers to Fixtures
Assignment
Modifiers are assigned per-channel, per-fixture:
// From fixture.h:323-328
void setChannelModifier ( quint32 idx , ChannelModifier * mod );
ChannelModifier * channelModifier ( quint32 idx );
Select Fixture
Choose the fixture you want to modify in the Fixture Manager.
Choose Channel
Select the specific channel to apply the modifier to (e.g., channel 0 = Dimmer).
Select or Create Modifier
Use a system template
Load a user template
Create a new custom modifier
Apply and Test
Apply the modifier and test the fixture’s response.
Storage
Modifiers are stored with the fixture in the project:
// From fixture.h:352-356
QMap < quint32, ChannelModifier *> m_channelModifiers;
Each fixture can have different modifiers on different channels.
Modifier Templates
System Templates
QLC+ includes built-in modifier templates for common use cases:
enum Type {
SystemTemplate = 0 , // Pre-installed modifiers
UserTemplate = 1 // User-created
};
System templates might include:
Invert : Full 0-255 inversion
Square Law : Quadratic dimming curve
Logarithmic : Log-based dimming
Exponential : Exponential response
User Templates
Create and save your own templates for reuse:
// From channelmodifier.h:67-72
QFile :: FileError saveXML ( const QString & fileName ) const ;
QFile :: FileError loadXML ( const QString & fileName , Type type );
Modifiers are saved as XML files:
<? xml version = "1.0" encoding = "UTF-8" ?>
<! DOCTYPE ChannelModifier >
< ChannelModifier >
< Name > Custom Dimmer Curve </ Name >
< Handler Original = "0" Modified = "0" />
< Handler Original = "25" Modified = "60" />
< Handler Original = "128" Modified = "140" />
< Handler Original = "255" Modified = "255" />
</ ChannelModifier >
XML Structure
// From channelmodifier.h:32-38 and channelmodifier.cpp:100-137
#define KXMLQLCChannelModifierDocument "ChannelModifier"
#define KXMLQLCChannelModName "Name"
#define KXMLQLCChannelModHandler "Handler"
#define KXMLQLCChannelModOriginalDMX "Original"
#define KXMLQLCChannelModModifiedDMX "Modified"
The modifier’s descriptive name: < Name > Custom Dimmer Curve </ Name >
Each handler defines one control point: < Handler Original = "128" Modified = "140" />
Original : Input DMX value (0-255)
Modified : Output DMX value (0-255)
Creating Custom Modifiers
Linear Modification
Define Start and End
< Handler Original = "0" Modified = "0" />
< Handler Original = "255" Modified = "255" />
This creates no modification (identity).
Add Intermediate Points
< Handler Original = "0" Modified = "0" />
< Handler Original = "128" Modified = "160" />
< Handler Original = "255" Modified = "255" />
Boosts mid-range values.
Inversion Modifier
< ChannelModifier >
< Name > Invert </ Name >
< Handler Original = "0" Modified = "255" />
< Handler Original = "255" Modified = "0" />
</ ChannelModifier >
S-Curve Modifier
Smooth transition with slow start/end, fast middle:
< ChannelModifier >
< Name > S-Curve </ Name >
< Handler Original = "0" Modified = "0" />
< Handler Original = "64" Modified = "32" />
< Handler Original = "128" Modified = "128" />
< Handler Original = "192" Modified = "223" />
< Handler Original = "255" Modified = "255" />
</ ChannelModifier >
Compression Modifier
Limit output range (e.g., never go below 20% or above 90%):
< ChannelModifier >
< Name > 20-90% Range </ Name >
< Handler Original = "0" Modified = "51" /> <!-- 20% -->
< Handler Original = "255" Modified = "229" /> <!-- 90% -->
</ ChannelModifier >
Loading and Saving Modifiers
Save XML
// From channelmodifier.cpp:100-137
QFile :: FileError ChannelModifier :: saveXML ( const QString & fileName ) const
{
if ( fileName . isEmpty ())
return QFile ::OpenError;
QFile file (fileName);
if ( ! file . open ( QIODevice ::WriteOnly))
return file . error ();
QXmlStreamWriter doc ( & file);
doc . setAutoFormatting ( true );
doc . writeTextElement (KXMLQLCChannelModName, m_name);
for ( int i = 0 ; i < m_map . count (); i ++ )
{
QPair < uchar, uchar > mapElement = m_map . at (i);
doc . writeStartElement (KXMLQLCChannelModHandler);
doc . writeAttribute (KXMLQLCChannelModOriginalDMX,
QString :: number ( mapElement . first ));
doc . writeAttribute (KXMLQLCChannelModModifiedDMX,
QString :: number ( mapElement . second ));
doc . writeEndElement ();
}
file . close ();
return QFile ::NoError;
}
Load XML
// From channelmodifier.cpp:139-212
QFile :: FileError ChannelModifier :: loadXML ( const QString & fileName , Type type )
{
if ( fileName . isEmpty ())
return QFile ::OpenError;
QXmlStreamReader * doc = QLCFile :: getXMLReader (fileName);
if (doc == NULL || doc -> hasError ())
return QFile ::ReadError;
QList < QPair < uchar, uchar >> modMap;
while ( ! doc -> atEnd ())
{
if ( doc -> name () == KXMLQLCChannelModName)
{
setName ( doc -> readElementText ());
}
else if ( doc -> name () == KXMLQLCChannelModHandler)
{
QPair < uchar, uchar > dmxPair ( 0 , 0 );
QXmlStreamAttributes attrs = doc -> attributes ();
if ( attrs . hasAttribute (KXMLQLCChannelModOriginalDMX))
dmxPair . first = attrs . value (KXMLQLCChannelModOriginalDMX)
. toString (). toUInt ();
if ( attrs . hasAttribute (KXMLQLCChannelModModifiedDMX))
dmxPair . second = attrs . value (KXMLQLCChannelModModifiedDMX)
. toString (). toUInt ();
modMap . append (dmxPair);
}
}
if ( modMap . count () > 0 )
{
setType (type);
setModifierMap (modMap);
}
return QFile ::NoError;
}
Value Retrieval
At runtime, modifiers transform values:
// From channelmodifier.cpp:95-98
uchar ChannelModifier :: getValue ( uchar dmxValue ) const
{
return m_values . at (dmxValue);
}
The pre-calculated lookup table (m_values) provides fast O(1) value transformation.
Practical Examples
Example 1: Fix LED Dimming
Problem : LED fixture jumps from 0 to bright at low values.
Solution : Create a modifier that spreads low values:
< ChannelModifier >
< Name > Smooth LED Dimmer </ Name >
< Handler Original = "0" Modified = "0" />
< Handler Original = "10" Modified = "30" />
< Handler Original = "50" Modified = "80" />
< Handler Original = "128" Modified = "150" />
< Handler Original = "255" Modified = "255" />
</ ChannelModifier >
Example 2: Match Fixture Brightness
Problem : Two fixtures of different models; one is brighter than the other at the same DMX value.
Solution : Reduce maximum output of the brighter fixture:
< ChannelModifier >
< Name > Reduce Brightness 20% </ Name >
< Handler Original = "0" Modified = "0" />
< Handler Original = "255" Modified = "204" /> <!-- 80% of 255 -->
</ ChannelModifier >
Example 3: Reverse Pan Direction
Problem : Moving head mounted backwards needs inverted pan.
Solution : Simple inversion:
< ChannelModifier >
< Name > Invert Pan </ Name >
< Handler Original = "0" Modified = "255" />
< Handler Original = "255" Modified = "0" />
</ ChannelModifier >
Apply to the Pan channel only; Tilt remains normal.
Example 4: Limit Range for Safety
Problem : Don’t want a motor-driven effect to reach extreme positions.
Solution : Compress to safe range (e.g., 25%-75%):
< ChannelModifier >
< Name > Safe Range Limit </ Name >
< Handler Original = "0" Modified = "64" /> <!-- 25% -->
< Handler Original = "255" Modified = "191" /> <!-- 75% -->
</ ChannelModifier >
Modifier Storage in Projects
Channel modifiers are saved per-fixture in the project XML:
< Fixture ID = "1" Name = "LED Par 1" Universe = "0" Address = "0" Channels = "4" >
< Modifier Channel = "0" Name = "Smooth LED Dimmer" />
</ Fixture >
The modifier definition itself is stored in:
System templates: QLC+ installation directory
User templates: User configuration directory
Advanced Techniques
Multi-Point Curves
Use many control points for precise curves:
< ChannelModifier >
< Name > Precise Curve </ Name >
< Handler Original = "0" Modified = "0" />
< Handler Original = "10" Modified = "25" />
< Handler Original = "20" Modified = "45" />
< Handler Original = "40" Modified = "75" />
< Handler Original = "80" Modified = "120" />
< Handler Original = "128" Modified = "160" />
< Handler Original = "192" Modified = "210" />
< Handler Original = "255" Modified = "255" />
</ ChannelModifier >
Step Functions
Create discrete steps (though interpolation makes true steps difficult):
< ChannelModifier >
< Name > Quarter Steps </ Name >
< Handler Original = "0" Modified = "0" />
< Handler Original = "63" Modified = "0" />
< Handler Original = "64" Modified = "85" />
< Handler Original = "127" Modified = "85" />
< Handler Original = "128" Modified = "170" />
< Handler Original = "191" Modified = "170" />
< Handler Original = "192" Modified = "255" />
< Handler Original = "255" Modified = "255" />
</ ChannelModifier >
Best Practices
Test with actual fixtures before using in production
Use QLC+‘s DMX monitor to verify output values
Document why each modifier was created
Keep backup copies of working modifiers
Use descriptive names: “Invert Pan”, “Smooth Dimmer”
Include fixture model if fixture-specific
Reference the channel: “Red Channel Boost”
Date complex modifiers: “LED Bar Fix 2024-03”
Always define handlers at 0 and 255
Use more points for complex curves
Space points where curve changes most
Test edge cases (0, 1, 254, 255)
Save useful modifiers as templates
Create a library of common corrections
Share modifiers with team members
Document parameters in the modifier name
Troubleshooting
Modifier Not Applied
Verify modifier is assigned to correct channel number
Check that fixture has the channel you’re modifying
Reload the project or refresh fixtures
Unexpected Output Values
Review all control points in the modifier
Check for typos in Original/Modified values
Remember: values are interpolated between points
Use DMX monitor to see actual output
Modifiers use pre-calculated lookup tables, so performance impact is minimal. If you experience issues:
Check total number of fixtures with modifiers
Verify QLC+ version supports modifiers properly
Test with fewer fixtures to isolate the problem
Next Steps