From: Stefan F. <st...@sf...> - 2010-06-07 14:48:49
|
Hi again, after reading the source all and all over again (and still without understanding every single aspect of it, so my wild guesses may be all completely wrong/dump), I start to think that this might be a logic bug. >From what I can tell, now, the mixer should recursively iterate through all the channels, from parents to childs to the leaves until it finds channels which only do depend on leaves (audio-tracks). These can be rendered to the buffer as all dependencies are known for these channels. Then it should go back to the parents of these channels and render them if all dependencies are met, too. ... so far so good. But this (from my current analysis -- as stated above it may be all wrong at all), is not what is happening with the current code. The current code does not go up to the leaves but only to the input channels of the current channel (which is not the same) and renders these. By doing so, it is -- from my point of view -- impossible to say that all source-channels of the current FX-Channel are really ready to be processed... void FxChannel::doProcessing( sampleFrame * _buf ) { FxMixer * fxm = engine::fxMixer(); const fpp_t fpp = engine::getMixer()->framesPerPeriod(); // <tobydox> ignore the passed _buf // <tobydox> always use m_buffer // <tobydox> this is just an auxilliary buffer if doProcessing() // needs one for processing while running // <tobydox> particularly important for playHandles, so Instruments // can operate on this buffer the whole time // <tobydox> this improves cache hit rate _buf = m_buffer; if( ! m_muteModel.value() ) { // do mixer sends. loop through the channels that send to this one for( int i = 0; i < m_receives.size(); ++i) { fx_ch_t senderIndex = m_receives[i]; FxChannel * sender = fxm->effectChannel(senderIndex); // mix it with this one float amt = fxm->channelSendModel(senderIndex, m_channelIndex)->value(); CPU::bufMixCoeff( _buf, sender->m_buffer, sender->m_volumeModel.value() * amt, fpp ); } Ok, we have gone through all the other FX-channels which send to us... but how do we know that really all of the input-channels are ready at that point? As far as I can tell, we can't... In most of the cases using simple mixer-setups it can resolve all of the inputs /but/ due to the general out-of-order nature of the underlying threads, it is (from my point of view) uncertain that this is allways the case. ... And this would correspond to the fact that these "clicks" do not happen always. I suppose (wildly guessing) they just happen if this is executed out of order... const float v = m_volumeModel.value(); m_fxChain.startRunning(); m_stillRunning = m_fxChain.processAudioBuffer( _buf, fpp ); m_peakLeft = engine::getMixer()->peakValueLeft( _buf, fpp ) * v; m_peakRight = engine::getMixer()->peakValueRight( _buf, fpp ) * v; } else { m_peakLeft = m_peakRight = 0.0f; } m_state = ThreadableJob::Done; // check if any of its parents are now able to be processed for(int i=0; i<m_sends.size(); ++i) { // if parent.unstarted and every parent.leaf.done: FxChannel * parent = fxm->effectChannel(m_sends[i]); if( parent->state() == ThreadableJob::Unstarted ) { bool everyLeafDone = true; for( int j=0; j<parent->m_receives.size(); ++j ) { if( fxm->effectChannel( parent->m_receives[j] )->state() != ThreadableJob::Done ) { everyLeafDone = false; break; } } This little change here does stabelize things a lot here on my machine... I am however not completely sure, why... ;-) My guess: it prevents a parent from being run too early... However the clicks are not completely gone (and can't by this modification as it does not change anything on the dependecies-tracking inside the channel-tree...) but a lot less often. if( everyLeafDone && i==(m_sends.size()-1) ) { MixerWorkerThread::addJob(parent); } } } } >From my point of view -- as stated above to be taken with some salt and grain -- the mixer follows an intermixed push-pull model (maybe accidently), currently while it clearly should follow a straigth pull-model... cu Stefan |