diff options
Diffstat (limited to 'muse2/muse/node.cpp')
-rw-r--r-- | muse2/muse/node.cpp | 895 |
1 files changed, 613 insertions, 282 deletions
diff --git a/muse2/muse/node.cpp b/muse2/muse/node.cpp index 9ecd8a0a..73495d8f 100644 --- a/muse2/muse/node.cpp +++ b/muse2/muse/node.cpp @@ -4,7 +4,7 @@ // $Id: node.cpp,v 1.36.2.25 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -364,13 +364,414 @@ void Track::setOff(bool val) _off = val; } +//--------------------------------------------------------- +// applyTrackCtrls +// If trackChans is 0, just process controllers only, not audio (do not 'run'). +//--------------------------------------------------------- + +void AudioTrack::processTrackCtrls(unsigned pos, int trackChans, unsigned nframes, float** buffer) +{ + const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); + const unsigned long min_per = (MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; + unsigned long sample = 0; + + const AutomationType at = automationType(); + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + CtrlListList* cll = controller(); + CtrlList* vol_ctrl = 0; + CtrlList* pan_ctrl = 0; + { + ciCtrlList icl = cll->find(AC_VOLUME); + if(icl == cll->end()) + return; + vol_ctrl = icl->second; + icl = cll->find(AC_PAN); + if(icl == cll->end()) + return; + pan_ctrl = icl->second; + } + + int cur_slice = 0; + while(sample < nframes) + { + unsigned long nsamp = nframes - sample; + const unsigned long slice_frame = pos + sample; + + // Process automation control values, while also determining the maximum acceptable + // size of this run. Further processing, from FIFOs for example, can lower the size + // from there, but this section determines where the next highest maximum frame + // absolutely needs to be for smooth playback of the controller value stream... + // + if(trackChans != 0 && !_prefader) // Don't bother if we don't want to run, or prefader is on. + { + ciCtrlList icl = cll->begin(); + for(unsigned long k = 0; k < _controlPorts; ++k) + { + CtrlList* cl = (icl != cll->end() ? icl->second : NULL); + CtrlInterpolate& ci = _controls[k].interp; + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)) ) ) + { + if(cl && (unsigned long)cl->id() == k) + { + cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); + if(icl != cll->end()) + ++icl; + } + else + { + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = _controls[k].val; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; + } + } + else + { + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. + { + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + if(icl != cll->end()) + ++icl; + } + + if(MusEGlobal::audio->isPlaying()) + { + unsigned long samps = nsamp; + if(ci.eFrame != -1) + samps = (unsigned long)ci.eFrame - slice_frame; + if(samps < nsamp) + nsamp = samps; + } + +#ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "AudioTrack::processTrackCtrls k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); +#endif + } + } + +#ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "AudioTrack::processTrackCtrls sample:%lu nsamp:%lu\n", sample, nsamp); +#endif + + // + // Process all control ring buffer items valid for this time period... + // + bool found = false; + unsigned long frame = 0; + unsigned long evframe; + while(!_controlFifo.isEmpty()) + { + ControlEvent v = _controlFifo.peek(); + // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. + // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. + evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; + + // Protection. Observed this condition. Why? Supposed to be linear timestamps. + if(found && evframe < frame) + { + fprintf(stderr, "AudioTrack::processTrackCtrls *** Error: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d\n", + evframe, v.frame, v.idx, v.value, v.unique); + + // No choice but to ignore it. + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + continue; + } + + if(evframe >= nframes // Next events are for a later period. + || (!found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) + || (found && !v.unique && (evframe - sample >= min_per))) // Eat up events within minimum slice - they're too close. + break; + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + + if(v.idx >= _controlPorts) // Sanity check + break; + + found = true; + frame = evframe; + + if(trackChans != 0 && !_prefader) // Only if we want to run, and prefader is off. + { + CtrlInterpolate* ci = &_controls[v.idx].interp; + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; + } + + // Need to update the automation value, otherwise it overwrites later with the last automation value. + setPluginCtrlVal(v.idx, v.value); + } + + if(found && trackChans != 0 && !_prefader) // If a control FIFO item was found, takes priority over automation controller stream. + nsamp = frame - sample; + + if(sample + nsamp > nframes) // Safety check. + nsamp = nframes - sample; + + // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. + // Note this means it is still possible to get stuck in the top loop (at least for a while). + if(nsamp != 0) + { + if(trackChans != 0 && !_prefader) + { + const CtrlInterpolate& vol_interp = _controls[AC_VOLUME].interp; + const CtrlInterpolate& pan_interp = _controls[AC_PAN].interp; + unsigned k; + //const float up_fact = 1.002711275; // 3.01.. dB / 256 + //const float down_fact = 0.997296056; + const float up_fact = 1.003471749; // 3.01.. dB / 200 + const float down_fact = 0.996540262; + float *sp1, *sp2, *dp1, *dp2; + float _volume, v, _pan, v1, v2; + + if(trackChans == 1) + { + float* sp = buffer[0] + sample; + float* dp = outBuffers[0] + sample; + sp1 = sp2 = sp; + dp1 = outBuffersExtraMix[0] + sample; + dp2 = outBuffersExtraMix[1] + sample; + k = 0; + if(vol_interp.doInterp && MusEGlobal::audio->isPlaying()) + { + for( ; k < nsamp; ++k) + { + _volume = vol_ctrl->interpolate(slice_frame + k, vol_interp); + v = _volume * _gain; + if(v > _curVolume) + { + if(_curVolume == 0.0) + _curVolume = 0.001; // Kick-start it from zero at -30dB. + _curVolume *= up_fact; + if(_curVolume >= v) + _curVolume = v; + } + else + if(v < _curVolume) + { + _curVolume *= down_fact; + if(_curVolume <= v || _curVolume <= 0.001) // Or if less than -30dB. + _curVolume = v; + } + *dp++ = *sp++ * _curVolume; + } + _controls[AC_VOLUME].val = _volume; // Update the port. + } + else + { + if(vol_interp.doInterp) // And not playing... + _volume = vol_ctrl->interpolate(pos, vol_interp); + else + _volume = vol_interp.sVal; + _controls[AC_VOLUME].val = _volume; // Update the port. + v = _volume * _gain; + if(v > _curVolume) + { + //fprintf(stderr, "A %f %f\n", v, _curVolume); + if(_curVolume == 0.0) + _curVolume = 0.001; // Kick-start it from zero at -30dB. + for( ; k < nsamp; ++k) + { + _curVolume *= up_fact; + if(_curVolume >= v) + { + _curVolume = v; + break; + } + *dp++ = *sp++ * _curVolume; + } + } + else + if(v < _curVolume) + { + //fprintf(stderr, "B %f %f\n", v, _curVolume); + for( ; k < nsamp; ++k) + { + _curVolume *= down_fact; + if(_curVolume <= v || _curVolume <= 0.001) // Or if less than -30dB. + { + _curVolume = v; + break; + } + *dp++ = *sp++ * _curVolume; + } + } + for( ; k < nsamp; ++k) + *dp++ = *sp++ * _curVolume; + } + } + else + if(trackChans >= 2) + { + sp1 = buffer[0] + sample; + sp2 = buffer[1] + sample; + dp1 = outBuffers[0] + sample; + dp2 = outBuffers[1] + sample; + } + + k = 0; + if((vol_interp.doInterp || pan_interp.doInterp) && MusEGlobal::audio->isPlaying()) + { + for( ; k < nsamp; ++k) + { + _volume = vol_ctrl->interpolate(slice_frame + k, vol_interp); + v = _volume * _gain; + _pan = pan_ctrl->interpolate(slice_frame + k, pan_interp); + v1 = v * (1.0 - _pan); + v2 = v * (1.0 + _pan); + if(v1 > _curVol1) + { + //fprintf(stderr, "C %f %f \n", v1, _curVol1); + if(_curVol1 == 0.0) + _curVol1 = 0.001; // Kick-start it from zero at -30dB. + _curVol1 *= up_fact; + if(_curVol1 >= v1) + _curVol1 = v1; + } + else + if(v1 < _curVol1) + { + //fprintf(stderr, "D %f %f \n", v1, _curVol1); + _curVol1 *= down_fact; + if(_curVol1 <= v1 || _curVol1 <= 0.001) // Or if less than -30dB. + _curVol1 = v1; + } + *dp1++ = *sp1++ * _curVol1; + + if(v2 > _curVol2) + { + //fprintf(stderr, "E %f %f \n", v2, _curVol2); + if(_curVol2 == 0.0) + _curVol2 = 0.001; // Kick-start it from zero at -30dB. + _curVol2 *= up_fact; + if(_curVol2 >= v2) + _curVol2 = v2; + } + else + if(v2 < _curVol2) + { + //fprintf(stderr, "F %f %f \n", v2, _curVol2); + _curVol2 *= down_fact; + if(_curVol2 <= v2 || _curVol2 <= 0.001) // Or if less than -30dB. + _curVol2 = v2; + } + *dp2++ = *sp2++ * _curVol2; + } + _controls[AC_VOLUME].val = _volume; // Update the ports. + _controls[AC_PAN].val = _pan; + } + else + { + if(vol_interp.doInterp) // And not playing... + _volume = vol_ctrl->interpolate(pos, vol_interp); + else + _volume = vol_interp.sVal; + if(pan_interp.doInterp) // And not playing... + _pan = pan_ctrl->interpolate(pos, pan_interp); + else + _pan = pan_interp.sVal; + _controls[AC_VOLUME].val = _volume; // Update the ports. + _controls[AC_PAN].val = _pan; + v = _volume * _gain; + v1 = v * (1.0 - _pan); + v2 = v * (1.0 + _pan); + if(v1 > _curVol1) + { + //fprintf(stderr, "C %f %f \n", v1, _curVol1); + if(_curVol1 == 0.0) + _curVol1 = 0.001; // Kick-start it from zero at -30dB. + for( ; k < nsamp; ++k) + { + _curVol1 *= up_fact; + if(_curVol1 >= v1) + { + _curVol1 = v1; + break; + } + *dp1++ = *sp1++ * _curVol1; + } + } + else + if(v1 < _curVol1) + { + //fprintf(stderr, "D %f %f \n", v1, _curVol1); + for( ; k < nsamp; ++k) + { + _curVol1 *= down_fact; + if(_curVol1 <= v1 || _curVol1 <= 0.001) // Or if less than -30dB. + { + _curVol1 = v1; + break; + } + *dp1++ = *sp1++ * _curVol1; + } + } + for( ; k < nsamp; ++k) + *dp1++ = *sp1++ * _curVol1; + + k = 0; + if(v2 > _curVol2) + { + //fprintf(stderr, "E %f %f \n", v2, _curVol2); + if(_curVol2 == 0.0) + _curVol2 = 0.001; // Kick-start it from zero at -30dB. + for( ; k < nsamp; ++k) + { + _curVol2 *= up_fact; + if(_curVol2 >= v2) + { + _curVol2 = v2; + break; + } + *dp2++ = *sp2++ * _curVol2; + } + } + else + if(v2 < _curVol2) + { + //fprintf(stderr, "F %f %f \n", v2, _curVol2); + for( ; k < nsamp; ++k) + { + _curVol2 *= down_fact; + if(_curVol2 <= v2 || _curVol2 <= 0.001) // Or if less than -30dB. + { + _curVol2 = v2; + break; + } + *dp2++ = *sp2++ * _curVol2; + } + } + for( ; k < nsamp; ++k) + *dp2++ = *sp2++ * _curVol2; + } + } + + sample += nsamp; + } + + ++cur_slice; // Slice is done. Moving on to any next slice now... + } +} //--------------------------------------------------------- // copyData //--------------------------------------------------------- // this is also addData(). addData() just calls copyData(..., true); -void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer, bool add /*=false*/) +void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer, bool add) { //Changed by T356. 12/12/09. // Overhaul and streamline to eliminate multiple processing during one process loop. @@ -385,11 +786,9 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s if(srcStartChan == -1) srcStartChan = 0; - int trackChans = channels(); + const int trackChans = channels(); int srcChans = (srcChannels == -1) ? trackChans : srcChannels; - int srcTotalOutChans = totalOutChannels(); - if(channels() == 1) - srcTotalOutChans = 1; + const int srcTotalOutChans = (channels() == 1) ? 1 : totalOutChannels(); // Special consideration for metronome: It is not part of the track list, // and it has no in or out routes, yet multiple output tracks may call addData on it! @@ -397,26 +796,14 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // Not strictly necessary here because only addData is ever called, but just to be consistent... int i; - - float* buffer[srcTotalOutChans]; + + float* buffer[srcTotalOutChans]; float data[nframes * srcTotalOutChans]; - - // precalculate stereo volume - double vol[2]; - double _volume = controller()->value(AC_VOLUME, pos, - !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); - double _pan = controller()->value(AC_PAN, pos, - !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl); - - vol[0] = _volume * (1.0 - _pan) * _gain; - vol[1] = _volume * (1.0 + _pan) * _gain; float meter[trackChans]; // Have we been here already during this process cycle? if(processed()) { - // If there is only one (or no) output routes, it's an error - we've been called more than once per process cycle! - // No, this is no longer an error, it's deliberate. Processing no longer done in 'chains', now done randomly. p4.0.37 #ifdef NODE_DEBUG_PROCESS printf("MusE: AudioTrack::copyData name:%s already processed _haveData:%d\n", name().toLatin1().constData(), _haveData); #endif @@ -424,9 +811,48 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // Is there already some data gathered from a previous call during this process cycle? if(_haveData) { - // Point the input buffers at our local cached 'pre-volume' buffers. They need processing, so continue on after. - for(i = 0; i < srcTotalOutChans; ++i) - buffer[i] = outBuffers[i]; + if(srcChans == dstChannels) + { + for(int c = 0; c < dstChannels; ++c) + { + float* sp = outBuffers[c + srcStartChan]; + float* dp = dstBuffer[c]; + if (!add) + AL::dsp->cpy(dp, sp, nframes); + else + for(unsigned k = 0; k < nframes; ++k) + *dp++ += *sp++; + } + } + else if(srcChans == 1 && dstChannels == 2) + { + for(int c = 0; c < dstChannels; ++c) + { + float* sp; + if(!_prefader && srcStartChan == 0 && trackChans == 1) + sp = outBuffersExtraMix[c]; // Use the pre-panned mono-to-stereo extra buffers. + else + sp = outBuffers[srcStartChan]; // In all other cases use the main buffers. + float* dp = dstBuffer[c]; + if (!add) + AL::dsp->cpy(dp, sp, nframes); + else + for(unsigned k = 0; k < nframes; ++k) + *dp++ += *sp++; + } + } + else if(srcChans == 2 && dstChannels == 1) + { + float* dp = dstBuffer[0]; + float* sp1 = outBuffers[srcStartChan]; + float* sp2 = outBuffers[srcStartChan + 1]; + if (!add) + for(unsigned k = 0; k < nframes; ++k) + *dp++ = (*sp1++ + *sp2++); + else + for(unsigned k = 0; k < nframes; ++k) + *dp++ += (*sp1++ + *sp2++); + } } else { @@ -446,9 +872,8 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s memset(dstBuffer[i], 0, sizeof(float) * nframes); } } - // else if (add) do nothing. - return; } + return; } else { @@ -463,7 +888,7 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s printf("MusE: AudioTrack::copyData name:%s dstChannels:%d Off, zeroing buffers\n", name().toLatin1().constData(), dstChannels); #endif - if (!add) + if(!add) { // Track is off. Zero the supplied buffers. unsigned int q; @@ -478,9 +903,9 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s memset(dstBuffer[i], 0, sizeof(float) * nframes); } } - // else if (add) do nothing - - _efxPipe->apply(0, nframes, 0); // Just process controls only, not audio (do not 'run'). + + _efxPipe->apply(pos, 0, nframes, 0); // Just process controls only, not audio (do not 'run'). + processTrackCtrls(pos, 0, nframes, 0); for(i = 0; i < trackChans; ++i) _meter[i] = 0.0; @@ -489,14 +914,14 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s } // Point the input buffers at a temporary stack buffer. - for(i = 0; i < srcTotalOutChans; ++i) + for(i = 0; i < srcTotalOutChans; ++i) buffer[i] = data + i * nframes; // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. // For ex. if this is an audio input, Jack will set the pointers for us in AudioInput::getData! // Don't do any processing at all if off. Whereas, mute needs to be ready for action at all times, // so still call getData before it. Off is NOT meant to be toggled rapidly, but mute is ! - if(!getData(pos, srcTotalOutChans, nframes, buffer) || (!add && isMute() && !_prefader)) + if(!getData(pos, srcTotalOutChans, nframes, buffer)) { #ifdef NODE_DEBUG_PROCESS printf("MusE: AudioTrack::copyData name:%s srcTotalOutChans:%d zeroing buffers\n", name().toLatin1().constData(), srcTotalOutChans); @@ -520,17 +945,75 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // apply plugin chain //--------------------------------------------------- - _efxPipe->apply(trackChans, nframes, buffer); + // Allow it to process even if muted so that when mute is turned off, left-over buffers (reverb tails etc) can die away. + _efxPipe->apply(pos, trackChans, nframes, buffer); //--------------------------------------------------- + // apply volume, pan + //--------------------------------------------------- + + #ifdef NODE_DEBUG_PROCESS + printf("MusE: AudioTrack::copyData trackChans:%d srcTotalOutChans:%d srcStartChan:%d srcChans:%d dstChannels:%d\n", trackChans, srcTotalOutChans, srcStartChan, srcChans, dstChannels); + #endif + + processTrackCtrls(pos, trackChans, nframes, buffer); + + const int valid_out_bufs = _prefader ? 0 : (trackChans >= 2 ? 2 : trackChans); + + //--------------------------------------------------- + // metering + //--------------------------------------------------- + + for(int c = 0; c < trackChans; ++c) + { + meter[c] = 0.0; + float* sp = (c >= valid_out_bufs) ? buffer[c] : outBuffers[c]; // Optimize: Don't all valid outBuffers just for meters + for(unsigned k = 0; k < nframes; ++k) + { + const double f = fabs(*sp++); // If the track is mono pan has no effect on meters. + if(f > meter[c]) + meter[c] = f; + } + _meter[c] = meter[c]; + if(_meter[c] > _peak[c]) + _peak[c] = _meter[c]; + } + + if(isMute()) + { + if (!add) + { + unsigned int q; + for(i = 0; i < dstChannels; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(q = 0; q < nframes; q++) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); + } + } + return; // We're outta here. + } + + // Copy whole blocks that we can get away with here outside of the track control processing loop. + for(i = valid_out_bufs; i < srcTotalOutChans; ++i) + AL::dsp->cpy(outBuffers[i], buffer[i], nframes); + + // We now have some data! Set to true. + _haveData = true; + + //--------------------------------------------------- // aux sends //--------------------------------------------------- - if(hasAuxSend() && !isMute()) + if(hasAuxSend()) { AuxList* al = MusEGlobal::song->auxs(); unsigned naux = al->size(); - for(unsigned k = 0; k < naux; ++k) + for(unsigned k = 0; k < naux; ++k) { float m = _auxSend[k]; if(m <= 0.0001) // optimize @@ -538,209 +1021,98 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s AudioAux* a = (AudioAux*)((*al)[k]); float** dst = a->sendBuffer(); int auxChannels = a->channels(); - if((srcChans ==1 && auxChannels==1) || srcChans == 2) + if((srcChans ==1 && auxChannels==1) || srcChans == 2) { - for(int ch = 0; ch < srcChans; ++ch) + for(int ch = 0; ch < srcChans; ++ch) { float* db = dst[ch % a->channels()]; // no matter whether there's one or two dst buffers - float* sb = buffer[ch]; - for(unsigned f = 0; f < nframes; ++f) - *db++ += (*sb++ * m * vol[ch]); // add to mix + float* sb = outBuffers[ch]; + for(unsigned f = 0; f < nframes; ++f) + *db++ += (*sb++ * m); // add to mix } } else if(srcChans==1 && auxChannels==2) // copy mono to both channels - { - for(int ch = 0; ch < auxChannels; ++ch) + { + for(int ch = 0; ch < auxChannels; ++ch) { float* db = dst[ch % a->channels()]; - float* sb = buffer[0]; - for(unsigned f = 0; f < nframes; ++f) - *db++ += (*sb++ * m * vol[ch]); // add to mix + float* sb = outBuffers[0]; + for(unsigned f = 0; f < nframes; ++f) + *db++ += (*sb++ * m); // add to mix } } } } //--------------------------------------------------- - // prefader metering + // copy to destination buffers //--------------------------------------------------- - if(_prefader) + // Sanity check. Is source starting channel out of range? Just zero and return. + if(srcStartChan >= srcTotalOutChans) { - for(i = 0; i < trackChans; ++i) + if(!add) { - float* p = buffer[i]; - meter[i] = 0.0; - for(unsigned k = 0; k < nframes; ++k) + unsigned int q; + for(i = 0; i < dstChannels; ++i) { - double f = fabs(*p++); - if(f > meter[i]) - meter[i] = f; - } - _meter[i] = meter[i]; - if(_meter[i] > _peak[i]) - _peak[i] = _meter[i]; - } - } - - if(isMute()) - { - if (!add) - { - unsigned int q; - for(i = 0; i < dstChannels; ++i) + if(MusEGlobal::config.useDenormalBias) { - if(MusEGlobal::config.useDenormalBias) - { - for(q = 0; q < nframes; q++) - dstBuffer[i][q] = MusEGlobal::denormalBias; - } - else - memset(dstBuffer[i], 0, sizeof(float) * nframes); - } + for(q = 0; q < nframes; q++) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); + } } - - if(!_prefader) - for(i = 0; i < trackChans; ++i) // Must process ALL channels, even if unconnected. Only max 2 channels. - _meter[i] = 0.0; - return; } - - // If we're using local cached 'pre-volume' buffers, copy the input buffers (as they are right now: post-effect pre-volume) back to them. - for(i = 0; i < srcTotalOutChans; ++i) - AL::dsp->cpy(outBuffers[i], buffer[i], nframes); - - // We have some data! Set to true. - _haveData = true; - } - - // Sanity check. Is source starting channel out of range? Just zero and return. - if(srcStartChan >= srcTotalOutChans) - { - unsigned int q; - for(i = 0; i < dstChannels; ++i) - { - if(MusEGlobal::config.useDenormalBias) - { - for(q = 0; q < nframes; q++) - dstBuffer[i][q] = MusEGlobal::denormalBias; - } - else - memset(dstBuffer[i], 0, sizeof(float) * nframes); - } - return; - } - // Force a source range to fit actual available total out channels. - if((srcStartChan + srcChans) > srcTotalOutChans) - srcChans = srcTotalOutChans - srcStartChan; - - //--------------------------------------------------- - // apply volume - // postfader metering - //--------------------------------------------------- + // Force a source range to fit actual available total out channels. + if((srcStartChan + srcChans) > srcTotalOutChans) + srcChans = srcTotalOutChans - srcStartChan; - #ifdef NODE_DEBUG_PROCESS - printf("MusE: AudioTrack::copyData trackChans:%d srcTotalOutChans:%d srcStartChan:%d srcChans:%d dstChannels:%d\n", trackChans, srcTotalOutChans, srcStartChan, srcChans, dstChannels); - #endif - - if(!_prefader) - { - for(int c = 0; c < trackChans; ++c) + if(srcChans == dstChannels) { - meter[c] = 0.0; - double v = (trackChans == 1 ? _volume : vol[c]); - float* sp = buffer[c]; - for(unsigned k = 0; k < nframes; ++k) - { - float val = *sp++ * v; // If the track is mono pan has no effect on meters. - double f = fabs(val); - if(f > meter[c]) - meter[c] = f; - } - _meter[c] = meter[c]; - if(_meter[c] > _peak[c]) - _peak[c] = _meter[c]; - } - } - - if(srcChans == dstChannels) - { - for(int c = 0; c < dstChannels; ++c) - { - double v; - if(srcStartChan > 2 || _prefader) // Don't apply pan or volume to extra channels above 2. Or if prefader on. - v = 1.0; - else - if(srcChans >= 2) // If 2 channels apply pan normally. - v = vol[c]; - else - if(trackChans < 2) // If 1 channel and track is 1 channel, don't apply pan. - v = _volume; - else - v = vol[srcStartChan]; // Otherwise 1 channel but track is 2 channels. Apply the channel volume. - - float* sp = buffer[c + srcStartChan]; - float* dp = dstBuffer[c]; - - if (!add) - { - for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp++ * v); - } - else // if (add) + for(int c = 0; c < dstChannels; ++c) { + float* sp = outBuffers[c + srcStartChan]; + float* dp = dstBuffer[c]; + if (!add) + AL::dsp->cpy(dp, sp, nframes); + else for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp++ * v); + *dp++ += *sp++; } } - } - else if(srcChans == 1 && dstChannels == 2) - { - for(int c = 0; c < dstChannels; ++c) + else if(srcChans == 1 && dstChannels == 2) { - double v; - if(srcStartChan > 2 || _prefader) // Don't apply pan or volume to extra channels above 2. Or if prefader on. - v = 1.0; - else - if(trackChans <= 1) // If track is mono apply pan. - v = vol[c]; - else - v = vol[srcStartChan]; // Otherwise track is stereo, apply the same channel volume to both. - - float* sp = buffer[srcStartChan]; - float* dp = dstBuffer[c]; - - if (!add) - { - for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp++ * v); - } - else // if (add) + for(int c = 0; c < dstChannels; ++c) { + float* sp; + if(!_prefader && srcStartChan == 0 && trackChans == 1) + sp = outBuffersExtraMix[c]; // Use the pre-panned mono-to-stereo extra buffers. + else + sp = outBuffers[srcStartChan]; // In all other cases use the main buffers. + float* dp = dstBuffer[c]; + if (!add) + AL::dsp->cpy(dp, sp, nframes); + else for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp++ * v); + *dp++ += *sp++; } } - } - else if(srcChans == 2 && dstChannels == 1) - { - double v1 = ((srcStartChan > 2 || _prefader) ? 1.0 : vol[srcStartChan]); // Don't apply pan or volume to extra channels above 2. Or if prefader on. - double v2 = ((srcStartChan > 2 || _prefader) ? 1.0 : vol[srcStartChan + 1]); // - float* dp = dstBuffer[0]; - float* sp1 = buffer[srcStartChan]; - float* sp2 = buffer[srcStartChan + 1]; - - if (!add) + else if(srcChans == 2 && dstChannels == 1) { + float* dp = dstBuffer[0]; + float* sp1 = outBuffers[srcStartChan]; + float* sp2 = outBuffers[srcStartChan + 1]; + if (!add) for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp1++ * v1 + *sp2++ * v2); - } - else // if (add) - { + *dp++ = (*sp1++ + *sp2++); + else for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp1++ * v1 + *sp2++ * v2); + *dp++ += (*sp1++ + *sp2++); } } } @@ -753,6 +1125,7 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr { copyData(pos,dstChannels,srcStartChan,srcChannels,nframes,dstBuffer, true); } + //--------------------------------------------------------- // readVolume //--------------------------------------------------------- @@ -785,67 +1158,6 @@ void AudioTrack::readVolume(Xml& xml) } } -// DELETETHIS 56 -// Removed by T356 -// "recfile" tag not saved anymore -/* - -THIS CODE IS OBSOLETE! _recFile has been changed from SndFile* to SndFileR. -this code has NOT been adapted! - -//--------------------------------------------------------- -// readRecfile -//--------------------------------------------------------- - -void AudioTrack::readRecfile(Xml& xml) - { - QString path; - int channels = 2; - int format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - - for (;;) { - Xml::Token token = xml.parse(); - if (token == Xml::Error || token == Xml::End) - break; - const QString& tag = xml.s1(); - switch (token) { - case Xml::TagStart: - if (tag == "path") - path = xml.parse1(); - else if (tag == "channels") - channels = xml.parseInt(); - else if (tag == "format") - format = xml.parseInt(); - else if (tag == "samplebits") - ; - else - xml.unknown("recfile"); - break; - case Xml::TagEnd: - if (tag == "recfile") { - if (QFile::exists(path)) { - setRecFile(getWave(path, true)); - } - else { - setRecFile(new SndFile(path)); - recFile()->setFormat(format, channels, sampleRate); - if (recFile()->openWrite()) { - fprintf(stderr, "create wave file(%s) failed: %s\n", - path.toLatin1().constData(), recFile()->strerror().toLatin1().constData()); - delete _recFile; - _recFile = 0; - } - } - return; - } - default: - break; - } - } - } -*/ - - //--------------------------------------------------------- // setChannels //--------------------------------------------------------- @@ -949,6 +1261,10 @@ bool AudioInput::getData(unsigned, int channels, unsigned nframes, float** buffe for (int ch = 0; ch < channels; ++ch) { void* jackPort = jackPorts[ch]; + + // REMOVE Tim. Just a test. + //if(jackPort) + // MusEGlobal::audioDevice->portLatency(jackPort, true); // Do not get buffers of unconnected client ports. Causes repeating leftover data, can be loud, or DC ! if (jackPort && MusEGlobal::audioDevice->connections(jackPort)) @@ -1183,6 +1499,9 @@ void AudioOutput::processInit(unsigned nframes) if (!MusEGlobal::checkAudioDevice()) return; for (int i = 0; i < channels(); ++i) { if (jackPorts[i]) { + + //MusEGlobal::audioDevice->portLatency(jackPorts[i], true); // REMOVE Tim. Just a test. + buffer[i] = MusEGlobal::audioDevice->getBuffer(jackPorts[i], nframes); if (MusEGlobal::config.useDenormalBias) { for (unsigned int j=0; j < nframes; j++) @@ -1452,6 +1771,23 @@ void Fifo::add() muse_atomic_inc(&count); } +//--------------------------------------------------------- +// setParam +//--------------------------------------------------------- + +void AudioTrack::setParam(unsigned long i, float val) +{ + addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame()); +} + +//--------------------------------------------------------- +// param +//--------------------------------------------------------- + +float AudioTrack::param(unsigned long i) const +{ + return _controls[i].val; +} //--------------------------------------------------------- // setChannels @@ -1473,35 +1809,30 @@ void AudioTrack::setTotalOutChannels(int num) int chans = _totalOutChannels; if(num != chans) { - // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. - if(chans < MAX_CHANNELS) - chans = MAX_CHANNELS; - if(outBuffers) - { - for(int i = 0; i < chans; ++i) - { - if(outBuffers[i]) - free(outBuffers[i]); - } - delete[] outBuffers; - } - _totalOutChannels = num; - chans = num; - // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + int new_chans = num; + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + if(new_chans < MAX_CHANNELS) + new_chans = MAX_CHANNELS; if(chans < MAX_CHANNELS) chans = MAX_CHANNELS; - - outBuffers = new float*[chans]; - for (int i = 0; i < chans; ++i) + if(new_chans != chans) { - int rv = posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); - if(rv != 0) + if(outBuffers) { - fprintf(stderr, "ERROR: AudioTrack::setTotalOutChannels: posix_memalign returned error:%d. Aborting!\n", rv); - abort(); + for(int i = 0; i < chans; ++i) + { + if(outBuffers[i]) + { + free(outBuffers[i]); + outBuffers[i] = NULL; + } + } + delete[] outBuffers; + outBuffers = NULL; } } + initBuffers(); } chans = num; // Limit the actual track (meters, copying etc, all 'normal' operation) to two-channel stereo. |