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
------------------------------------------------------------------------------
ThinkGeek and WIRED's GeekDad team up for the Ultimate
GeekDad Father's Day Giveaway. ONE MASSIVE PRIZE to the
lucky parental unit. See the prize list and enter to win:
http://p.sf.net/sfu/thinkgeek-promo
_______________________________________________
LMMS-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/lmms-devel