Skip to content

Commit

Permalink
remove autogenerated code from ChannelMixer
Browse files Browse the repository at this point in the history
Previously, the autogenerated code allowed for a vectorizing
optimization by combining gain application (channel faders +
crossfader) with channel mixing. When postfader effects were
implemented in Mixxx 2.1 (PR #1254), these two operations
had to be decoupled to process effects between gain
application and channel mixing. Therefore, the
autogenerated code lost its advantage and became an
overcomplication.

Unused autogenerated functions were removed from SampleUtil
as well. Some of the autogenerated SampleUtil functions are
still in use, so they were kept.
  • Loading branch information
Be-ing committed Dec 27, 2018
1 parent 89a2891 commit 6124eb2
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 23,104 deletions.
2 changes: 1 addition & 1 deletion build/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ def sources(self, build):
"src/engine/enginemicrophone.cpp",
"src/engine/enginedeck.cpp",
"src/engine/engineaux.cpp",
"src/engine/channelmixer_autogen.cpp",
"src/engine/channelmixer.cpp",

"src/engine/enginecontrol.cpp",
"src/engine/ratecontrol.cpp",
Expand Down
140 changes: 3 additions & 137 deletions scripts/generate_sample_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys

# To use, run this from the top level of the Git repository tree:
# scripts/generate_sample_functions.py --sample_autogen_h src/util/sample_autogen.h --channelmixer_autogen_cpp src/engine/channelmixer_autogen.cpp
# scripts/generate_sample_functions.py --sample_autogen_h src/util/sample_autogen.h

BASIC_INDENT = 4

Expand All @@ -25,130 +25,6 @@ def hanging_indent(base, groups, hanging_suffix, terminator, depth=0):
groups,
[hanging_suffix] * (len(groups) - 1) + [terminator])))

def write_channelmixer_autogen(output, num_channels):
output.append('#include "engine/channelmixer.h"')
output.append('#include "util/sample.h"')
output.append('#include "util/timer.h"')
output.append('////////////////////////////////////////////////////////')
output.append('// THIS FILE IS AUTO-GENERATED. DO NOT EDIT DIRECTLY! //')
output.append('// SEE scripts/generate_sample_functions.py //')
output.append('////////////////////////////////////////////////////////')
output.append('')
output.append('// static')

def write_applyeffectsandmixchannels(inplace, output):
header = 'void ChannelMixer::applyEffects%(inplace)sAndMixChannels(' % {'inplace': 'InPlace' if inplace else ''}
args = ['const EngineMaster::GainCalculator& gainCalculator',
'QVarLengthArray<EngineMaster::ChannelInfo*, kPreallocatedChannels>* activeChannels',
'QVarLengthArray<EngineMaster::GainCache, kPreallocatedChannels>* channelGainCache',
'CSAMPLE* pOutput', 'const ChannelHandle& outputHandle',
'unsigned int iBufferSize', 'unsigned int iSampleRate',
'EngineEffectsManager* pEngineEffectsManager']
output.extend(hanging_indent(header, args, ',', ') {'))

def write(data, depth=0):
output.append(' ' * (BASIC_INDENT * depth) + data)

if inplace:
write('// Signal flow overview:', depth=1)
write('// 1. Calculate gains for each channel', depth=1)
write('// 2. Pass each channel\'s calculated gain and input buffer to pEngineEffectsManager, which then:', depth=1)
write('// A) Applies the calculated gain to the channel buffer, modifying the original input buffer', depth=1)
write('// B) Applies effects to the buffer, modifying the original input buffer', depth=1)
write('// 4. Mix the channel buffers together to make pOutput, overwriting the pOutput buffer from the last engine callback', depth=1)
else:
write('// Signal flow overview:', depth=1)
write('// 1. Clear pOutput buffer', depth=1)
write('// 2. Calculate gains for each channel', depth=1)
write('// 3. Pass each channel\'s calculated gain and input buffer to pEngineEffectsManager, which then:', depth=1)
write('// A) Copies each channel input buffer to a temporary buffer', depth=1)
write('// B) Applies gain to the temporary buffer', depth=1)
write('// C) Processes effects on the temporary buffer', depth=1)
write('// D) Mixes the temporary buffer into pOutput', depth=1)
write('// The original channel input buffers are not modified.', depth=1)

write('int totalActive = activeChannels->size();', depth=1)
if not inplace:
write('SampleUtil::clear(pOutput, iBufferSize);', depth=1)
write('if (totalActive == 0) {', depth=1)
write('ScopedTimer t("EngineMaster::applyEffects%(inplace)sAndMixChannels_0active");' %
{'inplace': 'InPlace' if inplace else ''}, depth=2)
if inplace:
write('SampleUtil::clear(pOutput, iBufferSize);', depth=2)
for i in xrange(1, num_channels+1):
write('} else if (totalActive == %d) {' % i, depth=1)
write('ScopedTimer t("EngineMaster::applyEffects%(inplace)sAndMixChannels_%(i)dactive");' %
{'inplace': 'InPlace' if inplace else '', 'i': i}, depth=2)
write('CSAMPLE_GAIN oldGain[%(i)d];' % {'i': i}, depth=2)
write('CSAMPLE_GAIN newGain[%(i)d];' % {'i': i}, depth=2)
for j in xrange(i):
write('EngineMaster::ChannelInfo* pChannel%(j)d = activeChannels->at(%(j)d);' % {'j': j}, depth=2)
write('const int channelIndex%(j)d = pChannel%(j)d->m_index;' % {'j': j}, depth=2)
write('EngineMaster::GainCache& gainCache%(j)d = (*channelGainCache)[channelIndex%(j)d];' % {'j': j}, depth=2)
write('oldGain[%(j)d] = gainCache%(j)d.m_gain;' % {'j': j}, depth=2)
write('if (gainCache%(j)d.m_fadeout) {' % {'j': j}, depth=2)
write('newGain[%(j)d] = 0;' % {'j': j}, depth=3)
write('gainCache%(j)d.m_fadeout = false;' % {'j': j}, depth=3)
write('} else {', depth=2)
write('newGain[%(j)d] = gainCalculator.getGain(pChannel%(j)d);' % {'j': j}, depth=3)
write('}', depth=2)
write('gainCache%(j)d.m_gain = newGain[%(j)d];' % {'j': j}, depth=2)
write('CSAMPLE* pBuffer%(j)d = pChannel%(j)d->m_pBuffer;' % {'j': j}, depth=2)

if inplace:
write('// Process effects for each channel in place', depth=2)
else:
write('// Process effects for each channel and mix the processed signal into pOutput', depth=2)
for j in xrange(i):
if inplace:
write('pEngineEffectsManager->processPostFaderInPlace(pChannel%(j)d->m_handle, outputHandle, pBuffer%(j)d, iBufferSize, iSampleRate, pChannel%(j)d->m_features, oldGain[%(j)d], newGain[%(j)d]);' % {'j': j}, depth=2)
else:
write('pEngineEffectsManager->processPostFaderAndMix(pChannel%(j)d->m_handle, outputHandle, pBuffer%(j)d, pOutput, iBufferSize, iSampleRate, pChannel%(j)d->m_features, oldGain[%(j)d], newGain[%(j)d]);' % {'j': j}, depth=2)

if inplace:
write('// Mix the effected channel buffers together to replace the old pOutput from the last engine callback', depth=2)
write('for (unsigned int i = 0; i < iBufferSize; ++i) {', depth=2)
line = 'pOutput[i] = pBuffer0[i]'
for k in xrange(1, i):
line += ' + pBuffer%(k)d[i]' % {'k': k}
line += ';'
write(line, depth=3)
write('}', depth=2)

write('} else {', depth=1)
write('ScopedTimer t("EngineMaster::applyEffects%(inplace)sAndMixChannels_Over32active");' %
{'inplace': 'InPlace' if inplace else ''}, depth=2)
if inplace:
write('SampleUtil::clear(pOutput, iBufferSize);', depth=2)
write('for (int i = 0; i < activeChannels->size(); ++i) {', depth=2)
write('EngineMaster::ChannelInfo* pChannelInfo = activeChannels->at(i);', depth=3)

write('const int channelIndex = pChannelInfo->m_index;', depth=3)
write('EngineMaster::GainCache& gainCache = (*channelGainCache)[channelIndex];', depth=3)
write('CSAMPLE_GAIN oldGain = gainCache.m_gain;', depth=3)
write('CSAMPLE_GAIN newGain;', depth=3)
write('if (gainCache.m_fadeout) {', depth=3)
write('newGain = 0;', depth=4)
write('gainCache.m_fadeout = false;', depth=4)
write('} else {', depth=3)
write('newGain = gainCalculator.getGain(pChannelInfo);', depth=4)
write('}', depth=3)
write('gainCache.m_gain = newGain;', depth=3)

write('CSAMPLE* pBuffer = pChannelInfo->m_pBuffer;', depth=3)
if inplace:
write('pEngineEffectsManager->processPostFaderInPlace(pChannelInfo->m_handle, outputHandle, pBuffer, iBufferSize, iSampleRate, pChannelInfo->m_features, oldGain, newGain);' % {'j': j}, depth=3)
write('SampleUtil::add(pOutput, pBuffer, iBufferSize);', depth=3)
else:
write('pEngineEffectsManager->processPostFaderAndMix(pChannelInfo->m_handle, outputHandle, pBuffer, pOutput, iBufferSize, iSampleRate, pChannelInfo->m_features, oldGain, newGain);' % {'j': j}, depth=3)

write('}', depth=2)
write('}', depth=1)
output.append('}')

write_applyeffectsandmixchannels(False, output)
write_applyeffectsandmixchannels(True, output)

def write_sample_autogen(output, num_channels):
output.append('#ifndef MIXXX_UTIL_SAMPLEAUTOGEN_H')
output.append('#define MIXXX_UTIL_SAMPLEAUTOGEN_H')
Expand Down Expand Up @@ -254,22 +130,12 @@ def main(args):
if args.sample_autogen_h else sys.stdout)
output.write('\n'.join(sampleutil_output_lines) + '\n')

channelmixer_output_lines = []
write_channelmixer_autogen(channelmixer_output_lines, args.max_channels)

output = (open(args.channelmixer_autogen_cpp, 'w')
if args.channelmixer_autogen_cpp else sys.stdout)
output.write('\n'.join(channelmixer_output_lines) + '\n')



if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Auto-generate sample processing and mixing functions.' +
'Example Call:' +
'./generate_sample_functions.py --sample_autogen_h ../src/util/sample_autogen.h --channelmixer_autogen_cpp ../src/engine/channelmixer_autogen.cpp')
'./generate_sample_functions.py --sample_autogen_h ../src/util/sample_autogen.h')
parser.add_argument('--sample_autogen_h')
parser.add_argument('--channelmixer_autogen_cpp')
parser.add_argument('--max_channels', type=int, default=32)
parser.add_argument('--max_channels', type=int, default=3)
args = parser.parse_args()
main(args)
80 changes: 80 additions & 0 deletions src/engine/channelmixer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "engine/channelmixer.h"
#include "util/sample.h"
#include "util/timer.h"

// static
void ChannelMixer::applyEffectsAndMixChannels(const EngineMaster::GainCalculator& gainCalculator,
QVarLengthArray<EngineMaster::ChannelInfo*, kPreallocatedChannels>* activeChannels,
QVarLengthArray<EngineMaster::GainCache, kPreallocatedChannels>* channelGainCache,
CSAMPLE* pOutput,
const ChannelHandle& outputHandle,
unsigned int iBufferSize,
unsigned int iSampleRate,
EngineEffectsManager* pEngineEffectsManager) {
// Signal flow overview:
// 1. Clear pOutput buffer
// 2. Calculate gains for each channel
// 3. Pass each channel's calculated gain and input buffer to pEngineEffectsManager, which then:
// A) Copies each channel input buffer to a temporary buffer
// B) Applies gain to the temporary buffer
// C) Processes effects on the temporary buffer
// D) Mixes the temporary buffer into pOutput
// The original channel input buffers are not modified.
SampleUtil::clear(pOutput, iBufferSize);
ScopedTimer t("EngineMaster::applyEffectsAndMixChannels");
for (int i = 0; i < activeChannels->size(); ++i) {
EngineMaster::ChannelInfo* pChannelInfo = activeChannels->at(i);
EngineMaster::GainCache& gainCache = (*channelGainCache)[pChannelInfo->m_index];
CSAMPLE_GAIN oldGain = gainCache.m_gain;
CSAMPLE_GAIN newGain;
if (gainCache.m_fadeout) {
newGain = 0;
gainCache.m_fadeout = false;
} else {
newGain = gainCalculator.getGain(pChannelInfo);
}
gainCache.m_gain = newGain;
pEngineEffectsManager->processPostFaderAndMix(
pChannelInfo->m_handle, outputHandle,
pChannelInfo->m_pBuffer, pOutput,
iBufferSize, iSampleRate, pChannelInfo->m_features,
oldGain, newGain);
}
}

void ChannelMixer::applyEffectsInPlaceAndMixChannels(const EngineMaster::GainCalculator& gainCalculator,
QVarLengthArray<EngineMaster::ChannelInfo*, kPreallocatedChannels>* activeChannels,
QVarLengthArray<EngineMaster::GainCache, kPreallocatedChannels>* channelGainCache,
CSAMPLE* pOutput,
const ChannelHandle& outputHandle,
unsigned int iBufferSize,
unsigned int iSampleRate,
EngineEffectsManager* pEngineEffectsManager) {
// Signal flow overview:
// 1. Calculate gains for each channel
// 2. Pass each channel's calculated gain and input buffer to pEngineEffectsManager, which then:
// A) Applies the calculated gain to the channel buffer, modifying the original input buffer
// B) Applies effects to the buffer, modifying the original input buffer
// 4. Mix the channel buffers together to make pOutput, overwriting the pOutput buffer from the last engine callback
ScopedTimer t("EngineMaster::applyEffectsInPlaceAndMixChannels");
SampleUtil::clear(pOutput, iBufferSize);
for (int i = 0; i < activeChannels->size(); ++i) {
EngineMaster::ChannelInfo* pChannelInfo = activeChannels->at(i);
EngineMaster::GainCache& gainCache = (*channelGainCache)[pChannelInfo->m_index];
CSAMPLE_GAIN oldGain = gainCache.m_gain;
CSAMPLE_GAIN newGain;
if (gainCache.m_fadeout) {
newGain = 0;
gainCache.m_fadeout = false;
} else {
newGain = gainCalculator.getGain(pChannelInfo);
}
gainCache.m_gain = newGain;
pEngineEffectsManager->processPostFaderInPlace(
pChannelInfo->m_handle, outputHandle,
pChannelInfo->m_pBuffer,
iBufferSize, iSampleRate, pChannelInfo->m_features,
oldGain, newGain);
SampleUtil::add(pOutput, pChannelInfo->m_pBuffer, iBufferSize);
}
}
Loading

0 comments on commit 6124eb2

Please sign in to comment.