/* Read XBus data from serial port */ void readSerialPort(void) { /* Serial port and XBus message buffers */ unsigned char buff; unsigned int byte = 0; unsigned char message[XBUS_CHANNELS*4 + 7]; /* Read XBus input indefinitely */ while (1) { /* Get a character from the serial port */ int n = read(fd, &buff, 1); /* If we have new data, process it */ if (n == 1) { /* Check for start character */ if (byte == 0 && buff == 0xA4) { message[byte++] = buff; continue; } /* Assimilate the rest of the message */ if (byte > 0) { message[byte++] = buff; /* Full message seems to be two bytes (+ checksum) longer than length stated in the 2nd byte */ if (byte==message[1]+3) { // Couldn't figure out which bytes are actually being checksummed, so gave up trying. // TODO: Figure this out before doing any interface to flight controls (i.e safety critical stuff!) /* uint8_t crcCalc = crc8(message,message[1]); if (message[byte-1] == crcCalc) { // Process message } else { #ifdef DEBUG printf("XBus CRC Fail!\n"); #endif } */ // Assume that when we have a full packets worth of bytes (from start char), we can decode channels /* Assume a maximum of 14 channels (XG14 Tx) */ double channels[XBUS_CHANNELS]; for (int i=0; i<XBUS_CHANNELS; i++) { /* Conversion from JR Documentation */ channels[i] = 800.0 + (1400.0*(message[4 + i*4 + 2]*256+message[4 + i*4 + 3]))/(double(0xFFFF)); } /* Process the channel data */ processChannels(channels); /* Message has been deatl with, reset for next one */ byte = 0; } } } } }
void EngineMaster::process(const int iBufferSize) { static bool haveSetName = false; if (!haveSetName) { QThread::currentThread()->setObjectName("Engine"); haveSetName = true; } Trace t("EngineMaster::process"); bool masterEnabled = m_pMasterEnabled->get(); bool headphoneEnabled = m_pHeadphoneEnabled->get(); int iSampleRate = static_cast<int>(m_pMasterSampleRate->get()); // Update internal master sync. m_pMasterSync->onCallbackStart(iSampleRate, iBufferSize); if (m_pEngineEffectsManager) { m_pEngineEffectsManager->onCallbackStart(); } // Bitvector of enabled channels const unsigned int maxChannels = 32; unsigned int busChannelConnectionFlags[3] = { 0, 0, 0 }; unsigned int headphoneOutput = 0; // Prepare each channel for output processChannels(busChannelConnectionFlags, &headphoneOutput, iBufferSize); // Compute headphone mix // Head phone left/right mix CSAMPLE chead_gain = 1; CSAMPLE cmaster_gain = 0; if (masterEnabled) { CSAMPLE cf_val = m_pHeadMix->get(); chead_gain = 0.5 * (-cf_val + 1.); cmaster_gain = 0.5 * (cf_val + 1.); // qDebug() << "head val " << cf_val << ", head " << chead_gain // << ", master " << cmaster_gain; } // Mix all the enabled headphone channels together. m_headphoneGain.setGain(chead_gain); if (m_bRampingGain) { ChannelMixer::mixChannelsRamping( m_channels, m_headphoneGain, headphoneOutput, maxChannels, &m_channelHeadphoneGainCache, m_pHead, iBufferSize); } else { ChannelMixer::mixChannels( m_channels, m_headphoneGain, headphoneOutput, maxChannels, &m_channelHeadphoneGainCache, m_pHead, iBufferSize); } // Calculate the crossfader gains for left and right side of the crossfader double c1_gain, c2_gain; EngineXfader::getXfadeGains(m_pCrossfader->get(), m_pXFaderCurve->get(), m_pXFaderCalibration->get(), m_pXFaderMode->get() == MIXXX_XFADER_CONSTPWR, m_pXFaderReverse->get() == 1.0, &c1_gain, &c2_gain); // Channels with the talkover flag should be mixed with the master signal at // full master volume. All other channels should be adjusted by ducking gain. m_masterGain.setGains(m_pTalkoverDucking->getGain(iBufferSize / 2), c1_gain, 1.0, c2_gain, 1.0); // Make the mix for each output bus. m_masterGain takes care of applying the // master volume, the channel volume, and the orientation gain. for (int o = EngineChannel::LEFT; o <= EngineChannel::RIGHT; o++) { if (m_bRampingGain) { ChannelMixer::mixChannelsRamping( m_channels, m_masterGain, busChannelConnectionFlags[o], maxChannels, &m_channelMasterGainCache, m_pOutputBusBuffers[o], iBufferSize); } else { ChannelMixer::mixChannels( m_channels, m_masterGain, busChannelConnectionFlags[o], maxChannels, &m_channelMasterGainCache, m_pOutputBusBuffers[o], iBufferSize); } } // Process master channel effects if (m_pEngineEffectsManager) { GroupFeatureState busFeatures; m_pEngineEffectsManager->process(getBusLeftGroup(), m_pOutputBusBuffers[0], iBufferSize, busFeatures); m_pEngineEffectsManager->process(getBusCenterGroup(), m_pOutputBusBuffers[1], iBufferSize, busFeatures); m_pEngineEffectsManager->process(getBusRightGroup(), m_pOutputBusBuffers[2], iBufferSize, busFeatures); } if (masterEnabled) { // Mix the three channels together. We already mixed the busses together // with the channel gains and overall master gain. SampleUtil::copy3WithGain(m_pMaster, m_pOutputBusBuffers[EngineChannel::LEFT], 1.0, m_pOutputBusBuffers[EngineChannel::CENTER], 1.0, m_pOutputBusBuffers[EngineChannel::RIGHT], 1.0, iBufferSize); // Process master channel effects if (m_pEngineEffectsManager) { GroupFeatureState masterFeatures; // Well, this is delayed by one buffer (it's dependent on the // output). Oh well. if (m_pVumeter != NULL) { m_pVumeter->collectFeatures(&masterFeatures); } m_pEngineEffectsManager->process(getMasterGroup(), m_pMaster, iBufferSize, masterFeatures); } // Apply master volume after effects. CSAMPLE master_volume = m_pMasterVolume->get(); if (m_bRampingGain) { SampleUtil::applyRampingGain(m_pMaster, m_masterVolumeOld, master_volume, iBufferSize); } else { SampleUtil::applyGain(m_pMaster, master_volume, iBufferSize); } m_masterVolumeOld = master_volume; // Balance values CSAMPLE balright = 1.; CSAMPLE balleft = 1.; CSAMPLE bal = m_pBalance->get(); if (bal > 0.) { balleft -= bal; } else if (bal < 0.) { balright += bal; } // Perform balancing on main out SampleUtil::applyAlternatingGain(m_pMaster, balleft, balright, iBufferSize); // Update VU meter (it does not return anything). Needs to be here so that // master balance is reflected in the VU meter. if (m_pVumeter != NULL) { m_pVumeter->process(m_pMaster, iBufferSize); } // Submit master samples to the side chain to do shoutcasting, recording, // etc. (cpu intensive non-realtime tasks) if (m_pSideChain != NULL) { m_pSideChain->writeSamples(m_pMaster, iBufferSize); } // Add master to headphone with appropriate gain if (headphoneEnabled) { if (m_bRampingGain) { SampleUtil::addWithRampingGain(m_pHead, m_pMaster, m_headphoneMasterGainOld, cmaster_gain, iBufferSize); } else { SampleUtil::addWithGain(m_pHead, m_pMaster, cmaster_gain, iBufferSize); } m_headphoneMasterGainOld = cmaster_gain; } } if (headphoneEnabled) { // Process headphone channel effects if (m_pEngineEffectsManager) { GroupFeatureState headphoneFeatures; m_pEngineEffectsManager->process(getHeadphoneGroup(), m_pHead, iBufferSize, headphoneFeatures); } // Head volume CSAMPLE headphoneVolume = m_pHeadVolume->get(); if (m_bRampingGain) { SampleUtil::applyRampingGain(m_pHead, m_headphoneVolumeOld, headphoneVolume, iBufferSize); } else { SampleUtil::applyGain(m_pHead, headphoneVolume, iBufferSize); } m_headphoneVolumeOld = headphoneVolume; } if (masterEnabled && headphoneEnabled) { // If Head Split is enabled, replace the left channel of the pfl buffer // with a mono mix of the headphone buffer, and the right channel of the pfl // buffer with a mono mix of the master output buffer. if (m_pHeadSplitEnabled->get()) { for (int i = 0; i + 1 < iBufferSize; i += 2) { m_pHead[i] = (m_pHead[i] + m_pHead[i + 1]) / 2; m_pHead[i + 1] = (m_pMaster[i] + m_pMaster[i + 1]) / 2; } } } if (masterEnabled) { m_pMasterDelay->process(m_pMaster, iBufferSize); } else { SampleUtil::clear(m_pMaster, iBufferSize); } if (headphoneEnabled) { m_pHeadDelay->process(m_pHead, iBufferSize); } // We're close to the end of the callback. Wake up the engine worker // scheduler so that it runs the workers. m_pWorkerScheduler->runWorkers(); }
void EngineMaster::process(const int iBufferSize) { static bool haveSetName = false; if (!haveSetName) { QThread::currentThread()->setObjectName("Engine"); haveSetName = true; } Trace t("EngineMaster::process"); bool masterEnabled = m_pMasterEnabled->get(); bool boothEnabled = m_pBoothEnabled->get(); bool headphoneEnabled = m_pHeadphoneEnabled->get(); // TODO: remove assumption of stereo buffer const unsigned int kChannels = 2; const unsigned int iFrames = iBufferSize / kChannels; unsigned int iSampleRate = static_cast<int>(m_pMasterSampleRate->get()); if (m_pEngineEffectsManager) { m_pEngineEffectsManager->onCallbackStart(); } // Update internal master sync rate. m_pMasterSync->onCallbackStart(iSampleRate, iBufferSize); // Prepare each channel for output processChannels(iBufferSize); // Do internal master sync post-processing m_pMasterSync->onCallbackEnd(iSampleRate, iBufferSize); // Compute headphone mix // Head phone left/right mix CSAMPLE chead_gain = 1; CSAMPLE cmaster_gain = 0; if (masterEnabled) { CSAMPLE cf_val = m_pHeadMix->get(); chead_gain = 0.5 * (-cf_val + 1.); cmaster_gain = 0.5 * (cf_val + 1.); // qDebug() << "head val " << cf_val << ", head " << chead_gain // << ", master " << cmaster_gain; } // Mix all the PFL enabled channels together. m_headphoneGain.setGain(chead_gain); if (headphoneEnabled) { if (m_bRampingGain) { ChannelMixer::mixChannelsRamping( m_headphoneGain, &m_activeHeadphoneChannels, &m_channelHeadphoneGainCache, m_pHead, iBufferSize); } else { ChannelMixer::mixChannels( m_headphoneGain, &m_activeHeadphoneChannels, &m_channelHeadphoneGainCache, m_pHead, iBufferSize); } // Process headphone channel effects if (m_pEngineEffectsManager) { GroupFeatureState headphoneFeatures; m_pEngineEffectsManager->process(m_headphoneHandle.handle(), m_pHead, iBufferSize, iSampleRate, headphoneFeatures); } } // Mix all the talkover enabled channels together. if (m_bRampingGain) { ChannelMixer::mixChannelsRamping( m_talkoverGain, &m_activeTalkoverChannels, &m_channelTalkoverGainCache, m_pTalkover, iBufferSize); } else { ChannelMixer::mixChannels( m_talkoverGain, &m_activeTalkoverChannels, &m_channelTalkoverGainCache, m_pTalkover, iBufferSize); } // Clear talkover compressor for the next round of gain calculation. m_pTalkoverDucking->clearKeys(); if (m_pTalkoverDucking->getMode() != EngineTalkoverDucking::OFF) { m_pTalkoverDucking->processKey(m_pTalkover, iBufferSize); } // Calculate the crossfader gains for left and right side of the crossfader double crossfaderLeftGain, crossfaderRightGain; EngineXfader::getXfadeGains(m_pCrossfader->get(), m_pXFaderCurve->get(), m_pXFaderCalibration->get(), m_pXFaderMode->get(), m_pXFaderReverse->toBool(), &crossfaderLeftGain, &crossfaderRightGain); m_masterGain.setGains(crossfaderLeftGain, 1.0, crossfaderRightGain, m_pTalkoverDucking->getGain(iBufferSize / 2)); // Make the mix for each crossfader orientation output bus. // m_masterGain takes care of applying the attenuation from // channel volume faders, crossfader, and talkover ducking. // Talkover is mixed in later according to the configured MicMonitorMode for (int o = EngineChannel::LEFT; o <= EngineChannel::RIGHT; o++) { if (m_bRampingGain) { ChannelMixer::mixChannelsRamping( m_masterGain, &m_activeBusChannels[o], &m_channelMasterGainCache, // no [o] because the old gain follows an orientation switch m_pOutputBusBuffers[o], iBufferSize); } else { ChannelMixer::mixChannels( m_masterGain, &m_activeBusChannels[o], &m_channelMasterGainCache, m_pOutputBusBuffers[o], iBufferSize); } } // Process crossfader orientation bus channel effects if (m_pEngineEffectsManager) { GroupFeatureState busFeatures; m_pEngineEffectsManager->process(m_busLeftHandle.handle(), m_pOutputBusBuffers[EngineChannel::LEFT], iBufferSize, iSampleRate, busFeatures); m_pEngineEffectsManager->process(m_busCenterHandle.handle(), m_pOutputBusBuffers[EngineChannel::CENTER], iBufferSize, iSampleRate, busFeatures); m_pEngineEffectsManager->process(m_busRightHandle.handle(), m_pOutputBusBuffers[EngineChannel::RIGHT], iBufferSize, iSampleRate, busFeatures); } if (masterEnabled) { // Mix the crossfader orientation buffers together into the master mix SampleUtil::copy3WithGain(m_pMaster, m_pOutputBusBuffers[EngineChannel::LEFT], 1.0, m_pOutputBusBuffers[EngineChannel::CENTER], 1.0, m_pOutputBusBuffers[EngineChannel::RIGHT], 1.0, iBufferSize); MicMonitorMode configuredMicMonitorMode = static_cast<MicMonitorMode>( static_cast<int>(m_pMicMonitorMode->get())); // Process master, booth, and record/broadcast buffers according to the // MicMonitorMode configured in DlgPrefSound // TODO(Be): make SampleUtil ramping functions update the old gain variable if (configuredMicMonitorMode == MicMonitorMode::MASTER) { // Process master channel effects // TODO(Be): Move this after mixing in talkover. To apply master effects // to both the master and booth in that case will require refactoring // the effects system to be able to process the same effects on multiple // buffers within the same callback. applyMasterEffects(iBufferSize, iSampleRate); // Copy master mix to booth output with booth gain before mixing // talkover with master mix if (boothEnabled) { CSAMPLE boothGain = m_pBoothGain->get(); if (m_bRampingGain) { SampleUtil::copy1WithRampingGain(m_pBooth, m_pMaster, m_boothGainOld, boothGain, iBufferSize); } else { SampleUtil::copy1WithGain(m_pBooth, m_pMaster, boothGain, iBufferSize); } m_boothGainOld = boothGain; } // Mix talkover into master mix if (m_pNumMicsConfigured->get() > 0) { SampleUtil::copy2WithGain(m_pMaster, m_pMaster, 1.0, m_pTalkover, 1.0, iBufferSize); } // Apply master gain // TODO(Be): make this not affect the headphones. Refer to // https://bugs.launchpad.net/mixxx/+bug/1458213 CSAMPLE master_gain = m_pMasterGain->get(); if (m_bRampingGain) { SampleUtil::applyRampingGain(m_pMaster, m_masterGainOld, master_gain, iBufferSize); } else { SampleUtil::applyGain(m_pMaster, master_gain, iBufferSize); } m_masterGainOld = master_gain; // Record/broadcast signal is the same as the master output m_ppSidechainOutput = &m_pMaster; } else if (configuredMicMonitorMode == MicMonitorMode::MASTER_AND_BOOTH) { // Process master channel effects // TODO(Be): Move this after mixing in talkover. For the MASTER only // MicMonitorMode above, that will require refactoring the effects system // to be able to process the same effects on different buffers // within the same callback. For consistency between the MicMonitorModes, // process master effects here before mixing in talkover. applyMasterEffects(iBufferSize, iSampleRate); // Mix talkover with master if (m_pNumMicsConfigured->get() > 0) { SampleUtil::copy2WithGain(m_pMaster, m_pMaster, 1.0, m_pTalkover, 1.0, iBufferSize); } // Copy master mix (with talkover mixed in) to booth output with booth gain if (boothEnabled) { CSAMPLE boothGain = m_pBoothGain->get(); if (m_bRampingGain) { SampleUtil::copy1WithRampingGain(m_pBooth, m_pMaster, m_boothGainOld, boothGain, iBufferSize); } else { SampleUtil::copy1WithGain(m_pBooth, m_pMaster, boothGain, iBufferSize); } m_boothGainOld = boothGain; } // Apply master gain // TODO(Be): make this not affect the headphones. Refer to // https://bugs.launchpad.net/mixxx/+bug/1458213 CSAMPLE master_gain = m_pMasterGain->get(); if (m_bRampingGain) { SampleUtil::applyRampingGain(m_pMaster, m_masterGainOld, master_gain, iBufferSize); } else { SampleUtil::applyGain(m_pMaster, master_gain, iBufferSize); } m_masterGainOld = master_gain; // Record/broadcast signal is the same as the master output m_ppSidechainOutput = &m_pMaster; } else if (configuredMicMonitorMode == MicMonitorMode::DIRECT_MONITOR) { // Skip mixing talkover with the master and booth outputs // if using direct monitoring because it is being mixed in hardware // without the latency of sending the signal into Mixxx for processing. // However, include the talkover mix in the record/broadcast signal. // Copy master mix to booth output with booth gain if (boothEnabled) { CSAMPLE boothGain = m_pBoothGain->get(); if (m_bRampingGain) { SampleUtil::copy1WithRampingGain(m_pBooth, m_pMaster, m_boothGainOld, boothGain, iBufferSize); } else { SampleUtil::copy1WithGain(m_pBooth, m_pMaster, boothGain, iBufferSize); } m_boothGainOld = boothGain; } // Process master channel effects // NOTE(Be): This should occur before mixing in talkover for the // record/broadcast signal so the record/broadcast signal is the same // as what is heard on the master & booth outputs. applyMasterEffects(iBufferSize, iSampleRate); // Apply master gain // TODO(Be): make this not affect the headphones. Refer to // https://bugs.launchpad.net/mixxx/+bug/1458213 CSAMPLE master_gain = m_pMasterGain->get(); if (m_bRampingGain) { SampleUtil::applyRampingGain(m_pMaster, m_masterGainOld, master_gain, iBufferSize); } else { SampleUtil::applyGain(m_pMaster, master_gain, iBufferSize); } m_masterGainOld = master_gain; // The talkover signal Mixxx receives is delayed by the round trip latency. // There is an output latency between the time Mixxx processes the audio // and the user hears it. So if the microphone user plays on beat with // what they hear, they will be playing out of sync with the engine's // processing by the output latency. Additionally, Mixxx gets input signals // delayed by the input latency. By the time Mixxx receives the input signal, // a full round trip through the signal chain has elapsed since Mixxx // processed the output signal. // Although Mixxx receives the input signal delayed, the user hears it mixed // in hardware with the master & booth outputs without that // latency, so to record/broadcast the same signal that is heard // on the master & booth outputs, the master mix must be delayed before // mixing the talkover signal for the record/broadcast mix. // If not using microphone inputs or recording/broadcasting from // a sound card input, skip unnecessary processing here. if (m_pNumMicsConfigured->get() > 0 && !m_bExternalRecordBroadcastInputConnected) { // Copy the master mix to a separate buffer before delaying it // to avoid delaying the master output. SampleUtil::copy(m_pSidechainMix, m_pMaster, iBufferSize); m_pLatencyCompensationDelay->process(m_pSidechainMix, iBufferSize); SampleUtil::copy2WithGain(m_pSidechainMix, m_pSidechainMix, 1.0, m_pTalkover, 1.0, iBufferSize); m_ppSidechainOutput = &m_pSidechainMix; } else { m_ppSidechainOutput = &m_pMaster; } } // Submit buffer to the side chain to do broadcasting, recording, // etc. (CPU intensive non-realtime tasks) // If recording/broadcasting from a sound card input, // SoundManager will send the input buffer from the sound card to m_pSidechain // so skip sending a buffer to m_pSidechain here. if (!m_bExternalRecordBroadcastInputConnected && m_pEngineSideChain != nullptr) { m_pEngineSideChain->writeSamples(*m_ppSidechainOutput, iFrames); } // Balance values CSAMPLE balright = 1.; CSAMPLE balleft = 1.; CSAMPLE bal = m_pBalance->get(); if (bal > 0.) { balleft -= bal; } else if (bal < 0.) { balright += bal; } // Perform balancing on main out SampleUtil::applyAlternatingGain(m_pMaster, balleft, balright, iBufferSize); // Update VU meter (it does not return anything). Needs to be here so that // master balance and talkover is reflected in the VU meter. if (m_pVumeter != NULL) { m_pVumeter->process(m_pMaster, iBufferSize); } // Add master to headphone with appropriate gain if (headphoneEnabled) { if (m_bRampingGain) { SampleUtil::addWithRampingGain(m_pHead, m_pMaster, m_headphoneMasterGainOld, cmaster_gain, iBufferSize); } else { SampleUtil::addWithGain(m_pHead, m_pMaster, cmaster_gain, iBufferSize); } m_headphoneMasterGainOld = cmaster_gain; } } if (headphoneEnabled) { // Process headphone channel effects if (m_pEngineEffectsManager) { GroupFeatureState headphoneFeatures; m_pEngineEffectsManager->process(m_headphoneHandle.handle(), m_pHead, iBufferSize, iSampleRate, headphoneFeatures); } // Head volume CSAMPLE headphoneGain = m_pHeadGain->get(); if (m_bRampingGain) { SampleUtil::applyRampingGain(m_pHead, m_headphoneGainOld, headphoneGain, iBufferSize); } else { SampleUtil::applyGain(m_pHead, headphoneGain, iBufferSize); } m_headphoneGainOld = headphoneGain; } if (masterEnabled && headphoneEnabled) { // If Head Split is enabled, replace the left channel of the pfl buffer // with a mono mix of the headphone buffer, and the right channel of the pfl // buffer with a mono mix of the master output buffer. if (m_pHeadSplitEnabled->get()) { // note: NOT VECTORIZED because of in place copy for (int i = 0; i + 1 < iBufferSize; i += 2) { m_pHead[i] = (m_pHead[i] + m_pHead[i + 1]) / 2; m_pHead[i + 1] = (m_pMaster[i] + m_pMaster[i + 1]) / 2; } } } if (m_pMasterMonoMixdown->get()) { SampleUtil::mixStereoToMono(m_pMaster, m_pMaster, iBufferSize); } if (masterEnabled) { m_pMasterDelay->process(m_pMaster, iBufferSize); } else { SampleUtil::clear(m_pMaster, iBufferSize); } if (headphoneEnabled) { m_pHeadDelay->process(m_pHead, iBufferSize); } if (boothEnabled) { m_pBoothDelay->process(m_pBooth, iBufferSize); } // We're close to the end of the callback. Wake up the engine worker // scheduler so that it runs the workers. m_pWorkerScheduler->runWorkers(); }