summaryrefslogtreecommitdiff
path: root/muse2/muse/node.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse2/muse/node.cpp')
-rw-r--r--muse2/muse/node.cpp895
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.