void EngineMaster::processChannels(int iBufferSize) { m_activeBusChannels[EngineChannel::LEFT].clear(); m_activeBusChannels[EngineChannel::CENTER].clear(); m_activeBusChannels[EngineChannel::RIGHT].clear(); m_activeHeadphoneChannels.clear(); m_activeTalkoverChannels.clear(); m_activeChannels.clear(); ScopedTimer timer("EngineMaster::processChannels"); EngineChannel* pMasterChannel = m_pMasterSync->getMaster(); // Reserve the first place for the master channel which // should be processed first m_activeChannels.append(NULL); int activeChannelsStartIndex = 1; // Nothing at 0 yet for (int i = 0; i < m_channels.size(); ++i) { ChannelInfo* pChannelInfo = m_channels[i]; EngineChannel* pChannel = pChannelInfo->m_pChannel; // Skip inactive channels. if (!pChannel || !pChannel->isActive()) { continue; } if (pChannel->isTalkoverEnabled()) { // talkover is an exclusive channel // once talkover is enabled it is not used in // xFader-Mix m_activeTalkoverChannels.append(pChannelInfo); // Check if we need to fade out the master channel GainCache& gainCache = m_channelMasterGainCache[i]; if (gainCache.m_gain) { gainCache.m_fadeout = true; m_activeBusChannels[pChannel->getOrientation()].append(pChannelInfo); } } else { // Check if we need to fade out the channel GainCache& gainCache = m_channelTalkoverGainCache[i]; if (gainCache.m_gain) { gainCache.m_fadeout = true; m_activeTalkoverChannels.append(pChannelInfo); } if (pChannel->isMasterEnabled() && !pChannelInfo->m_pMuteControl->toBool()) { // the xFader-Mix m_activeBusChannels[pChannel->getOrientation()].append(pChannelInfo); } else { // Check if we need to fade out the channel GainCache& gainCache = m_channelMasterGainCache[i]; if (gainCache.m_gain) { gainCache.m_fadeout = true; m_activeBusChannels[pChannel->getOrientation()].append(pChannelInfo); } } } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPflEnabled()) { m_activeHeadphoneChannels.append(pChannelInfo); } else { // Check if we need to fade out the channel GainCache& gainCache = m_channelHeadphoneGainCache[i]; if (gainCache.m_gain) { m_channelHeadphoneGainCache[i].m_fadeout = true; m_activeHeadphoneChannels.append(pChannelInfo); } } // If necessary, add the channel to the list of buffers to process. if (pChannel == pMasterChannel) { // If this is the sync master, it should be processed first. m_activeChannels.replace(0, pChannelInfo); activeChannelsStartIndex = 0; } else { m_activeChannels.append(pChannelInfo); } } // Now that the list is built and ordered, do the processing. for (int i = activeChannelsStartIndex; i < m_activeChannels.size(); ++i) { ChannelInfo* pChannelInfo = m_activeChannels[i]; EngineChannel* pChannel = pChannelInfo->m_pChannel; pChannel->process(pChannelInfo->m_pBuffer, iBufferSize); } // After all the engines have been processed, trigger post-processing // which ensures that all channels are updating certain values at the // same point in time. This prevents sync from failing depending on // if the sync target was processed before or after the sync origin. for (int i = activeChannelsStartIndex; i < m_activeChannels.size(); ++i) { m_activeChannels[i]->m_pChannel->postProcess(iBufferSize); } }
void EngineMaster::processChannels(unsigned int* busChannelConnectionFlags, unsigned int* headphoneOutput, int iBufferSize) { ScopedTimer timer("EngineMaster::processChannels"); QList<ChannelInfo*>::iterator it = m_channels.begin(); QList<ChannelInfo*>::iterator master_it = NULL; // Clear talkover compressor for the next round of gain calculation. m_pTalkoverDucking->clearKeys(); // Find the Sync Master and process it first then process all the slaves // (and skip the master). EngineChannel* pMasterChannel = m_pMasterSync->getMaster(); if (pMasterChannel != NULL) { for (unsigned int channel_number = 0; it != m_channels.end(); ++it, ++channel_number) { ChannelInfo* pChannelInfo = *it; EngineChannel* pChannel = pChannelInfo->m_pChannel; if (!pChannel || !pChannel->isActive()) { continue; } if (pMasterChannel == pChannel) { master_it = it; // Proceed with the processing as below. bool needsProcessing = false; if (pChannel->isMaster()) { busChannelConnectionFlags[pChannel->getOrientation()] |= (1 << channel_number); needsProcessing = true; } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPFL()) { *headphoneOutput |= (1 << channel_number); needsProcessing = true; } // Process the buffer if necessary, which it damn well better be if (needsProcessing) { pChannel->process(pChannelInfo->m_pBuffer, iBufferSize); if (m_pTalkoverDucking->getMode() != EngineTalkoverDucking::OFF && pChannel->isTalkover()) { m_pTalkoverDucking->processKey(pChannelInfo->m_pBuffer, iBufferSize); } } break; } } } it = m_channels.begin(); for (unsigned int channel_number = 0; it != m_channels.end(); ++it, ++channel_number) { ChannelInfo* pChannelInfo = *it; EngineChannel* pChannel = pChannelInfo->m_pChannel; // Skip the master since we already processed it. if (it == master_it) { continue; } // Skip inactive channels. if (!pChannel || !pChannel->isActive()) { continue; } bool needsProcessing = false; if (pChannel->isMaster()) { busChannelConnectionFlags[pChannel->getOrientation()] |= (1 << channel_number); needsProcessing = true; } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPFL()) { *headphoneOutput |= (1 << channel_number); needsProcessing = true; } // Process the buffer if necessary if (needsProcessing) { pChannel->process(pChannelInfo->m_pBuffer, iBufferSize); if (m_pTalkoverDucking->getMode() != EngineTalkoverDucking::OFF && pChannel->isTalkover()) { m_pTalkoverDucking->processKey(pChannelInfo->m_pBuffer, iBufferSize); } } } }
void EngineMaster::process(const CSAMPLE *, const CSAMPLE *pOut, const int iBufferSize) { static bool haveSetName = false; if (!haveSetName) { QThread::currentThread()->setObjectName("Engine"); haveSetName = true; } ScopedTimer t("EngineMaster::process"); CSAMPLE **pOutput = (CSAMPLE**)pOut; Q_UNUSED(pOutput); // Prepare each channel for output // Bitvector of enabled channels const unsigned int maxChannels = 32; unsigned int masterOutput = 0; unsigned int headphoneOutput = 0; // Compute headphone mix // Head phone left/right mix float cf_val = head_mix->get(); float chead_gain = 0.5*(-cf_val+1.); float cmaster_gain = 0.5*(cf_val+1.); // qDebug() << "head val " << cf_val << ", head " << chead_gain // << ", master " << cmaster_gain; Timer timer("EngineMaster::process channels"); QList<ChannelInfo*>::iterator it = m_channels.begin(); for (unsigned int channel_number = 0; it != m_channels.end(); ++it, ++channel_number) { ChannelInfo* pChannelInfo = *it; EngineChannel* pChannel = pChannelInfo->m_pChannel; if (!pChannel->isActive()) { continue; } bool needsProcessing = false; if (pChannel->isMaster()) { masterOutput |= (1 << channel_number); needsProcessing = true; } // If the channel is enabled for previewing in headphones, copy it // over to the headphone buffer if (pChannel->isPFL()) { headphoneOutput |= (1 << channel_number); needsProcessing = true; } // Process the buffer if necessary if (needsProcessing) { pChannel->process(NULL, pChannelInfo->m_pBuffer, iBufferSize); } } timer.elapsed(true); // Mix all the enabled headphone channels together. m_headphoneGain.setGain(chead_gain); mixChannels(headphoneOutput, maxChannels, m_pHead, iBufferSize, &m_headphoneGain); // Calculate the crossfader gains for left and right side of the crossfader float c1_gain, c2_gain; EngineXfader::getXfadeGains(c1_gain, c2_gain, crossfader->get(), xFaderCurve->get(), xFaderCalibration->get(), xFaderMode->get()==MIXXX_XFADER_CONSTPWR, xFaderReverse->get()==1.0); // Now set the gains for overall volume and the left, center, right gains. m_masterGain.setGains(m_pMasterVolume->get(), c1_gain, 1.0, c2_gain); // Perform the master mix mixChannels(masterOutput, maxChannels, m_pMaster, iBufferSize, &m_masterGain); #ifdef __LADSPA__ // LADPSA master effects ladspa->process(m_pMaster, m_pMaster, iBufferSize); #endif // Clipping clipping->process(m_pMaster, m_pMaster, iBufferSize); // Balance values float balright = 1.; float balleft = 1.; float 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 (vumeter != NULL) vumeter->process(m_pMaster, 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 SampleUtil::addWithGain(m_pHead, m_pMaster, cmaster_gain, iBufferSize); // Head volume and clipping SampleUtil::applyGain(m_pHead, m_pHeadVolume->get(), iBufferSize); head_clipping->process(m_pHead, m_pHead, iBufferSize); //Master/headphones interleaving is now done in //SoundManager::requestBuffer() - Albert Nov 18/07 // We're close to the end of the callback. Wake up the engine worker // scheduler so that it runs the workers. m_pWorkerScheduler->runWorkers(); }