Old 08-18-2016, 09:22 PM   #41
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

Quote:
Originally Posted by Time Waster View Post
Yes, it will take one block to reach the new level. I think of it as the 'patina' of the system. I'm sure that there are hysteresis effects in hardware systems that preclude them from intstataneous attacke responses.
That's an aesthetic choice, and yours to make. But again, how modular do you want to be? How many of these parameters do you want to be static and hardcoded and which ones would you like to have a knob for, and which of those might be better as seperate modules that could affect anything you damn well please?

Quote:
Surely Reaper's parameter modulation is more convoluted than using MIDI directly? I would expect the Reaper's parameter modulation to be laggy, it's a compromise.
Have you tried it? You literally just wiggle the slider, go to the menu and select PM, link it to whatever CC (or whatever else!) you want, and....

...deal with what that does to the slider in your code. I honestly haven't actually tried your thing. I'm absolutely sure that it works exactly the way you expect and that's great! That's the fun part about JS. It's just that easy to make exactly the plugin that you need.
ashcat_lt is offline   Reply With Quote
Old 08-18-2016, 10:25 PM   #42
Time Waster
Human being with feelings
 
Time Waster's Avatar
 
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
Default

Quote:
Originally Posted by ashcat_lt View Post

Have you tried it? You literally just wiggle the slider, go to the menu and select PM, link it to whatever CC (or whatever else!) you want,.
Sorry, I wasn't being clear, I meant convoluted as in how parameter modulation accesses the VST or whatever. It's an assumption on my part that that must be more complex (and laggy) than having a dedicated system like MIDI.
__________________
Mal, aka The Wasters of Time
Mal's JSFX: ReaRack2 Modular Synth
Time Waster is offline   Reply With Quote
Old 08-18-2016, 10:58 PM   #43
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by jcjr View Post
By selecting the proper "critically damped" Q on a second order filter, maybe a second order filter would do a little better job on time-domain smoothing. Never investigated it so far.
I believe a 2nd order low-pass filter with Q=0.5 has exactly the same response as 2 cascaded 1st order low-pass filters.
Tale is online now   Reply With Quote
Old 08-19-2016, 01:53 AM   #44
Time Waster
Human being with feelings
 
Time Waster's Avatar
 
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
Default

I had an idea: If you really can't wait the 3 to 10 ms required for the attack to happen, you could have a slider for "Attack Rate Fine Tune" by which you could reduce the number of iterations to a percentage of the block size. The trade off would be more noise, but that might be a benefit for those really punchy fast attacks.
__________________
Mal, aka The Wasters of Time
Mal's JSFX: ReaRack2 Modular Synth
Time Waster is offline   Reply With Quote
Old 08-19-2016, 02:04 AM   #45
teatime
Human being with feelings
 
teatime's Avatar
 
Join Date: Aug 2016
Location: South Africa
Posts: 44
Default

@jcrc

ookkkaaayyy, my bad. I think you are absolutely correct.

The net effect of a PID controller would be the same as a lowpass - it imparts a certain transfer function which would have the effect of smoothing out high frequency changes (given good tuning). I think I'm right in saying a 6dB LP's transfer function is of the form 1/(first order polinomial) (eg. for simplicity 1/(s+1) )

Given a PID controller takes the form 1/(second order polinomial), i think it's fair to say that a PID controller works like a second order lowpass.

That is good, learn something new e'ery day.

Just for interest, IIRC the reason the controller has a P, I and D component are as follows:
P - Makes up the error faster for bigger errors.
I - P only never reaches the set-point on its own, you have to integrate the tiny remaining error to get to err=0
D - PI controller alone tend to have wildly oscillating behaviour; the D component tames the response and stops it from overshooting too much.

Since 2nd Order LP ~= PID Controller, there should be analogs for all three terms, though they may be encoded somehow in the cutoff and resonance - I don't know if theres a third term I don't know about.

May be talking out of my ass, but given the laplace transform is just a generalised fourier transform, it can be seen as the frequency domain companion to a time domain system; so generally applicable to many fields.

Good info, thanks.
teatime is offline   Reply With Quote
Old 08-19-2016, 09:32 AM   #46
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

Quote:
Originally Posted by Time Waster View Post
Sorry, I wasn't being clear, I meant convoluted as in how parameter modulation accesses the VST or whatever. It's an assumption on my part that that must be more complex (and laggy) than having a dedicated system like MIDI.
Again I have to ask if you've tried it. I can't think of a good reason that it has to be "laggy".

I have to admit that I haven't done a lot with the actual MIDI Link. I have messed with MIDI Control, but im not sure that's not a completely different thing. The times I used it it just worked, but I wasn't checking the timing accuracy to the precision we're talking about here. It worked fine for faders and footpedals, but...

I have done some testing with Parameter Linking and from what I saw there isn't really any lag in that system. Just the stair stepping that happens when you do things in blocks.
ashcat_lt is offline   Reply With Quote
Old 08-19-2016, 12:31 PM   #47
jcjr
Human being with feelings
 
Join Date: Dec 2015
Location: SE TN USA
Posts: 77
Default

Quote:
Originally Posted by Tale View Post
I believe a 2nd order low-pass filter with Q=0.5 has exactly the same response as 2 cascaded 1st order low-pass filters.
Thanks much Tale. I thought that was the case but my memory is bad and couldn't recall for sure.

If overshoot is not to be tolerated, perhaps the choice between two cascaded first order vs one Q = 0.5 second order could be "which one calculates faster"?
jcjr is offline   Reply With Quote
Old 08-20-2016, 12:27 AM   #48
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by jcjr View Post
If overshoot is not to be tolerated, perhaps the choice between two cascaded first order vs one Q = 0.5 second order could be "which one calculates faster"?
Exactly! Two cascaded 1st order filters will almost certainly be faster.
Tale is online now   Reply With Quote
Old 08-20-2016, 09:33 AM   #49
Time Waster
Human being with feelings
 
Time Waster's Avatar
 
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
Default

Quote:
Originally Posted by ashcat_lt View Post
Again I have to ask if you've tried it. I can't think of a good reason that it has to be "laggy".

I have to admit that I haven't done a lot with the actual MIDI Link. I have messed with MIDI Control, but im not sure that's not a completely different thing. The times I used it it just worked, but I wasn't checking the timing accuracy to the precision we're talking about here. It worked fine for faders and footpedals, but...

I have done some testing with Parameter Linking and from what I saw there isn't really any lag in that system. Just the stair stepping that happens when you do things in blocks.
I have used parameter linking, but not much with the MIDI link. I have found parameter linking to be perfectly adequate for anything I've tried to do. However, I have assumed that a system that modulates parameters by somehow moving sliders and knobs is not going to be as efficient as one which uses a more direct method of accessing the parameters. Of course, I have no idea how it actually works, so it is a naive assumption.
__________________
Mal, aka The Wasters of Time
Mal's JSFX: ReaRack2 Modular Synth
Time Waster is offline   Reply With Quote
Old 08-20-2016, 12:50 PM   #50
jcjr
Human being with feelings
 
Join Date: Dec 2015
Location: SE TN USA
Posts: 77
Default

Quote:
Originally Posted by teatime View Post
Given a PID controller takes the form 1/(second order polinomial), i think it's fair to say that a PID controller works like a second order lowpass.

That is good, learn something new e'ery day.

Just for interest, IIRC the reason the controller has a P, I and D component are as follows:
P - Makes up the error faster for bigger errors.
I - P only never reaches the set-point on its own, you have to integrate the tiny remaining error to get to err=0
D - PI controller alone tend to have wildly oscillating behaviour; the D component tames the response and stops it from overshooting too much.

Since 2nd Order LP ~= PID Controller, there should be analogs for all three terms, though they may be encoded somehow in the cutoff and resonance - I don't know if theres a third term I don't know about.

May be talking out of my ass, but given the laplace transform is just a generalised fourier transform, it can be seen as the frequency domain companion to a time domain system; so generally applicable to many fields.
Thanks Teatime. I'm rather foggy on math, taking a long time and resulting in unreliable answers which require testing. If a 2nd order lowpass h(s) equation would be discovered "very similar" to a PID h(s) equation then am guessing that both would be PID? I haven't a clue.

Passive analog filters with inductors and capacitors achieve Q by storing energy and releasing it a little later. The inductor and capacitor "swap energy back and forth with each other" in a damped oscillatory fashion.

Many active analog filters use controlled amounts of positive feedback to "add delayed energy" and increase the Q. The digital filters seem to do the same but it is subtler to pick out. Usually the digital second order filters use two integrated delays rather than capacitors and inductors.

Maybe the "delayed positive feedback" to raise Q, is how the D of PID happens? If in fact a second order filter is "nearly the same" as PID, which I do not know.

That PID way of thinking about it may be useful for dynamics processors. Some compressors and such are smart enough to adapt their attack and release, and sometimes other parameters, according to the nature of the signal. Thinking about that process in terms of PID may be productive.

Smoothing in compressors has similarities to smoothing of "instantaneous change" control signals, or synth envelope generators, or crossfading audio.

If the transition happens too quick it makes audio clicks, and if the transition lasts more than about 10 or 20 ms and is done "wrong" the ear notices that as well.

Also, the "wrong shape" of the smoothed transition can cause intermodulation distortion or aliasing. Any "sharp edged" control change, when applied to audio, can make transient frequencies above nyquist, which get reflected back into the audio band to nasty things up.

For instance simple linear interpolation, perhaps drawing a straight line over 128 samples from OldLevelA up to NewLevelB-- The beginning and end of the linear interpolation straight line are sharp sudden angles. Not as sharp as an instant step from A to B, but sharp enough IN SOME CASES to be heard in the audio.

Was looking at the response of that first-order smoother object I posted earlier, feeding it random stepped DC levels. It will probably attenuate gross audio clicks and zipper noise, but does not look ideal. The end of every smoothed transition is smooth, but the very beginning of each smoothed transition is angular.

Two or more first order smoothers in series can "round off" both the beginning and end of a sudden transition step.

In a limiter I was toying with, which at one point generates straight-edged ramps between limiting vs no-limiting-- Feeding the linear interpolation straight lines through a short first-order filter rounded off the corners on each end of the linear interpolation segments.

Maybe in some cases a linear interpolator + first order filter would work better than a cascade of first order filters.
jcjr is offline   Reply With Quote
Old 08-20-2016, 04:01 PM   #51
Smashed Transistors
Human being with feelings
 
Smashed Transistors's Avatar
 
Join Date: Jul 2014
Location: Là bas les huîtres (FR)
Posts: 424
Default

Discontinuity of values and discontinuities of derivatives have different spectrum.
The more you have continuous derivatives, the less you have clicks.
Factually: the clicks lose -6dB per octave per continuous derivative.

A discontinuity of value may generate a click. (typically an amplitude envelope).
A discontinuity of it's derivative may produce some "clack" (typically frequency change of an oscillator while preserving its phase).
__________________
JSFX plugins and synths. See you here and there: SoundCloud, Youtube, Google Play...
Smashed Transistors is offline   Reply With Quote
Old 08-21-2016, 04:03 AM   #52
Time Waster
Human being with feelings
 
Time Waster's Avatar
 
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
Default

Quote:
Originally Posted by Smashed Transistors View Post
Discontinuity of values and discontinuities of derivatives have different spectrum.
The more you have continuous derivatives, the less you have clicks.
Factually: the clicks lose -6dB per octave per continuous derivative.

A discontinuity of value may generate a click. (typically an amplitude envelope).
A discontinuity of it's derivative may produce some "clack" (typically frequency change of an oscillator while preserving its phase).
One thing I was thinking of doing was to replace the liner interpolation between stair steps with an 'S' shaped interpolation (using a cosine function). Would this approach be along the lines of what you are suggesting above? My objective would be to reduce the number of iterations required, to address the attack latency issue.
__________________
Mal, aka The Wasters of Time
Mal's JSFX: ReaRack2 Modular Synth
Time Waster is offline   Reply With Quote
Old 08-21-2016, 06:25 AM   #53
Smashed Transistors
Human being with feelings
 
Smashed Transistors's Avatar
 
Join Date: Jul 2014
Location: Là bas les huîtres (FR)
Posts: 424
Default

Cosines will induce smooth steps were you want to have something more like a straight line.

You can chain two first order filters as suggested by Tale.

You can also use a second order low pass/smoothing filter that does not overshoot and preserve the shape of the envelope.

I think that the best choice would be a Bessel/Thiran filter as it has a flat group delay.
http://upir.ir/aban93/5_77d0e.pdf.
__________________
JSFX plugins and synths. See you here and there: SoundCloud, Youtube, Google Play...
Smashed Transistors is offline   Reply With Quote
Old 08-21-2016, 05:36 PM   #54
Time Waster
Human being with feelings
 
Time Waster's Avatar
 
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
Default

Filters are a bit beyond my current skill set, and I was hoping to avoid having to implement one, but I guess there is only one way to learn...
__________________
Mal, aka The Wasters of Time
Mal's JSFX: ReaRack2 Modular Synth
Time Waster is offline   Reply With Quote
Old 08-21-2016, 06:09 PM   #55
jcjr
Human being with feelings
 
Join Date: Dec 2015
Location: SE TN USA
Posts: 77
Default

Thanks for the good ideas, Smashed Transistors!
jcjr is offline   Reply With Quote
Old 08-21-2016, 07:35 PM   #56
Time Waster
Human being with feelings
 
Time Waster's Avatar
 
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
Default

Quote:
Originally Posted by Smashed Transistors View Post

I think that the best choice would be a Bessel/Thiran filter as it has a flat group delay.
http://upir.ir/aban93/5_77d0e.pdf.
OK, so after attempting to read the referenced paper, I can determine that I won't be adding a filter to my amp module any time soon. I can just about understand moving average filters, but that's about the limit of my maths knowledge. If anyone is interested in helping me out with adding a filter to my basic code, I would be most appreciative. Otherwise, it's steps all the way!

One thing I should mention is that because I'm building a modular system, there may be many instances of this amp running, so I think the code needs to be kept fairly efficient. I don't really have much of a feel for how adding a filter will affect that?
__________________
Mal, aka The Wasters of Time
Mal's JSFX: ReaRack2 Modular Synth
Time Waster is offline   Reply With Quote
Old 08-23-2016, 07:36 PM   #57
Time Waster
Human being with feelings
 
Time Waster's Avatar
 
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
Default

Sorry to keep banging on about this, let me know if you want me to shut up. Still on the stepwise method, I tested the cosine interpolation theory and I found it to be significantly noisier than linear interpolation (there is a chance that my maths is wrong, so I've posted the code below for checking).

If the results are correct, this would suggest that the noise is a result of the steepness of the curve (the rate of change in volume), rather than, as I had thought, originating from points where the curve is not smooth. If this is true, then linear interpolation will always give the least noise for the least number of samples. In my testing (which is subjective) I found that I get relatively noise free results interpolating over 20 samples, but I have opted for 50 samples in my final code. This is about 2 milliseconds reaction time at 48kHz. In my subjective opinion, the stepwise nature of the volume change is not noticeable, as long as you can minimise or eliminate the noise at each step.

Cosine interpolation code:
Code:
@block

modcc = slider1; 
  
while (midirecv(offset, msg1, msg2, msg3))
(  
  // Extract message type
  status = msg1 & $xF0;    
  // Is it a controller event?
  status == $xB0 ?
  (
    // Is it the right CC?
    msg2 == modcc ?
    (
     modval = msg3;
     modval = 0 ? modlevel = 0: 
     modlevel = 2^((((modval/127)-1)*60)/6);
     modstep = modlevel - prelevel;
    );
  );
  // Pass thru
  midisend(offset, msg1, msg2, msg3); 
);
outlevel = prelevel;
prelevel = modlevel;
count = 0;
@sample
count <= 50? outlevel = prelevel+(modstep*(1-((cos($pi*(count/50))/2)+0.5))):
             outlevel = prelevel + modstep;
spl0=spl0*outlevel;
spl1=spl1*outlevel;
count += 1;
__________________
Mal, aka The Wasters of Time
Mal's JSFX: ReaRack2 Modular Synth

Last edited by Time Waster; 08-23-2016 at 07:42 PM.
Time Waster is offline   Reply With Quote
Old 08-24-2016, 12:15 PM   #58
jcjr
Human being with feelings
 
Join Date: Dec 2015
Location: SE TN USA
Posts: 77
Default

I have not closely studied "how fast you can fade" and get away without clicks, thumps or other bad sounds. An expert might be able to do a few differential equations and tell us. It probably depends on what we are fading.

Another thing I noticed from practical experience-- It is somewhat random when the ear will hear a problem. Sometimes if you look at a zoomed-in processed waveform it looks smooth to the eye but the ear still hears a problem, and other times the processed audio waveform might look rough as a cob but the ear doesn't hear anything bad in the sound.

So if "pushing the limits" a short smoothing might work just fine 9 times out of 10 or even 99 times out of 100, but occasionally a bad artifact happens.

Years ago I worked on an application which did MANY MANY audio crossfades. Beatmap re-composition and modest stretching. For instance, an audio file in memory with 96 bars of a good drummer. One array with sample pointers to the beginning of each bar, and another bigger array with sample pointers to every significant auto-detected "beat" transient peak, each drum or cymbal hit.

If you wanted to play the sequence of bars 1, 5, 6, 2, 6, 3, etc, then the play pointer would crossfade to jump to the new locations at the proper times to play the rearranged drum sequence. If you wanted to play it a little faster or slower, it would crossfade-jump between different beatmap drum hits to stretch time without affecting the fidelity. This does not work good for heavy stretching but is very transparent for modest amounts of stretch.

So anyway, in extreme cases it was crossfades on-top of nested crossfades.

A 10 ms linear crossfade seemed fairly bullet-proof for transparent playback in that kind of situation. In some other situation maybe a longer or shorter transition time would work.
jcjr is offline   Reply With Quote
Old 08-24-2016, 02:45 PM   #59
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

I worked at the other (the midi input) site of the thingy and now - to be used as a Volume-control - it has slider to set the dB per CC step value.

The linear part at the low end of the curve (slope and break point) is automatically adapted so that the slope of the exponmential part at the break point is identical.

The JSFX shows a graphic of that curve and the point selected by the incoming CC value.

-Michael

Code:
// Author: Michael Schnell, based on a work of Time Waster (M. Smith)
// License: LGPL - http://www.gnu.org/licenses/lgpl.html
//
// the mnudi CC values 0 ... 127 are mapped to an "aplifyer" curve that consists of a lineat and an exponentil part
// with CC = 127 the amplification is 1
// the slider defines the amout (in dB) the amplification is reduces  for each CC step.
// the breakpoint between the exponential and the linear part is set so that at this point the value and the slope 
// of the curves match.
// Below the breakpoint a linear curve is used so that with CC = 0 the aplification is Zero (-infinity dB).


desc:Midi Volume Control

slider1:0<0,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>MIDI Input Channel
slider2:1<0,127,1{0 Bank Sel M,1 Mod Wheel M,2 Breath M,3,4 Foot P M,5 Porta M,6 Data Entry M,7 Vol M,8 Balance M,9,10 Pan M,11 Expression M,12 Ctrl 1 M,13 Ctrl 2 M,14,15,16 GP Slider 1,17 GP Slider 2,18 GP Slider 3,19 GP Slider 4,20,21,22,23,24,25,26,27,28,29,30,31,32 Bank Sel L,33 Mod Wheel L,34 Breath L,35,36 Foot P L,37 Porta L,38 Data Entry L,39 Vol L,40 Balance L,41,42 Pan L,43 Expression L,44 Ctrl 1 L,45 Ctrl 2 L,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64 Hold P sw,65 Porta sw,66 Sustenuto sw,67 Soft P sw,68 Legato P sw,69 Hold 2 P sw,70 S.Variation,71 S.Timbre,72 S.Release,73 S.Attack,74 S.Brightness,75 S.Ctrl 6,76 S.Ctrl 7,77 S.Ctrl 8,78 S.Ctrl 9,79 S.Ctrl 10,80 GP B.1 sw,81 GP B.2 sw,82 GP B.3 sw,83 GP B.4 sw,84,85,86,87,88,89,90,91 Effects Lv,92 Trem Lv,93 Chorus Lv,94 Celeste Lv,95 Phaser Lv,96 Data B. Inc,97 Data B. Dec,98 NRP L,99 NRP M,100 RP L,101 RP M,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127}>CC Input
slider3:-0.3<-1,-0.1,0.01>Attenuation per CC step
//slider4:0<0,1,0.01>Factor (Test)

@init
  modval      = 0;
  prelevel    = 0;
  maxCCvalue  = 127;
  r4          = log(10)/20;
  
  function f (x) (
    x < limit  ? (
      x*r3;
     ) : (
      exp((maxCCvalue-x) * dbperstep*r4);
    );
  )

@slider
  inChannel   = slider1;
  modcc       = slider2; 
  dbperstep   = slider3;
  db20perstep = dbperstep/20;
  p1          = 1 / log(10) / db20perstep;
  p2          = 1 / maxCCvalue;
  p3          = p1 + p2;
  limit       = (-p3+0.5) |0; 
  r1          = (maxCCvalue-limit) * dbperstep;
  r2          = exp(log(10)*r1/20);
  r3          = r2 / limit;  
  
@block
  while (midirecv(offset, msg1, msg2, msg3)) (
    status = msg1 & $xF0;      // Extract message type
    channel = msg1 & $x0F;
    channel == inChannel ? (   // Is it on our channel?
      status == $xB0 ? (       // Is it a controller event?
        msg2 == modcc ? (      // Is it the right CC?
        modval = msg3;
          msg3 > 0 ? (
            modlevel = f(modval);
            modstep  = exp( log(modlevel / prelevel) / samplesblock);
            outlevel = prelevel;
            prelevel = modlevel;
            count = 0;
           ) : (
            modlevel = 0; 
            outlevel = 0;
            count = 10000;
          );   
//        slider4 = modlevel;                        // test
        );
      );
    );
    midisend(offset, msg1, msg2, msg3); // pass through
  );
@sample
  count <= samplesblock ? (
    outlevel *= modstep;
    count += 1;
  );  
  spl0*=outlevel;
  spl1*=outlevel;  
  
  
@gfx 640 400

gfx_r=gfx_g=gfx_b=0; gfx_a=1;
gfx_x=gfx_y=0;
gfx_rectto(gfx_w,gfx_h);

q1 = gfx_w / maxCCvalue;
q2 = gfx_h;


//gfx_line();
gfx_r=gfx_g=gfx_b=1;
gfx_y = 0;
gfx_x = 0;
x = 0;
while (x<=maxCCvalue) (
  a = x*q1;
  b = gfx_h - f(x)*q2;
  gfx_lineto(a, b, 1);
  x = x+1;
);
gfx_y = 0;
gfx_x = modval*q1;
gfx_lineto(gfx_x, gfx_h);
gfx_x = 0;
gfx_y = gfx_h-modlevel*q2;   
gfx_lineto(gfx_w, gfx_y);
mschnell is offline   Reply With Quote
Old 08-25-2016, 02:55 PM   #60
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

I could not resist and enhanced the code by a filter that IMHO is very appropriate for Volume Control via Midi.

I simply limited the exponential step width for the amplification by a value that (in dB) can be set via a slider. Of course now the count of steps until the value is reached will be more than the count of samples in a block (and independent of same). The code in @sample was not enhanced to make this happen, so nearly no CPU time necessary for that smoothing enhancement .

If the step limit is set rather low, the graphic shows how the amplification moves smoothly to the final value that is defined by the CC value (and the setting of the other slider).

-Michael

Code:
// Author: Michael Schnell, based on a work of Time Waster (M. Smith)
// License: LGPL - http://www.gnu.org/licenses/lgpl.html
//
// the mnudi CC values 0 ... 127 are mapped to an "aplifyer" curve that consists of a lineat and an exponentil part
// with CC = 127 the amplification is 1
// the slider defines the amout (in dB) the amplification is reduces  for each CC step.
// the breakpoint between the exponential and the linear part is set so that at this point the value and the slope 
// of the curves match.
// Below the breakpoint a linear curve is used so that with CC = 0 the aplification is Zero (-infinity dB).


desc:Midi Volume Control

slider1:0<0,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>MIDI Input Channel
slider2:1<0,127,1{0 Bank Sel M,1 Mod Wheel M,2 Breath M,3,4 Foot P M,5 Porta M,6 Data Entry M,7 Vol M,8 Balance M,9,10 Pan M,11 Expression M,12 Ctrl 1 M,13 Ctrl 2 M,14,15,16 GP Slider 1,17 GP Slider 2,18 GP Slider 3,19 GP Slider 4,20,21,22,23,24,25,26,27,28,29,30,31,32 Bank Sel L,33 Mod Wheel L,34 Breath L,35,36 Foot P L,37 Porta L,38 Data Entry L,39 Vol L,40 Balance L,41,42 Pan L,43 Expression L,44 Ctrl 1 L,45 Ctrl 2 L,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64 Hold P sw,65 Porta sw,66 Sustenuto sw,67 Soft P sw,68 Legato P sw,69 Hold 2 P sw,70 S.Variation,71 S.Timbre,72 S.Release,73 S.Attack,74 S.Brightness,75 S.Ctrl 6,76 S.Ctrl 7,77 S.Ctrl 8,78 S.Ctrl 9,79 S.Ctrl 10,80 GP B.1 sw,81 GP B.2 sw,82 GP B.3 sw,83 GP B.4 sw,84,85,86,87,88,89,90,91 Effects Lv,92 Trem Lv,93 Chorus Lv,94 Celeste Lv,95 Phaser Lv,96 Data B. Inc,97 Data B. Dec,98 NRP L,99 NRP M,100 RP L,101 RP M,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127}>CC Input
slider3:-0.3<-1,-0.1,0.01>Attenuation per CC step
slider4:0.003<0.0005, 0.003, 0.0001>max step(dB)
//slider5:0<0,1,0.01>Factor (Test)

@init
  modval      = 0;
//  prelevel    = 0;
  maxCCvalue  = 127;
  r4          = log(10)/20;
  
  function f (x) (
    x < limit  ? (
      x*r3;
     ) : (
      exp((maxCCvalue-x) * r5);
    );
  )

@slider
  inChannel   = slider1;
  modcc       = slider2; 
  dbperstep   = slider3;
  db20perstep = dbperstep/20;
  p1          = 1 / log(10) / db20perstep;
  p2          = 1 / maxCCvalue;
  p3          = p1 + p2;
  limit       = (-p3+0.5) |0; 
  r1          = (maxCCvalue-limit) * dbperstep;
  r2          = exp(log(10)*r1/20);
  r3          = r2 / limit;  
  r5          = dbperstep*r4;
  s1          = exp(log(10)*slider4/20);
  s2          = 1 / s1;
  f1          = f(1);
  
@block
  while (midirecv(offset, msg1, msg2, msg3)) (
    status = msg1 & $xF0;      // Extract message type
    channel = msg1 & $x0F;
    channel == inChannel ? (   // Is it on our channel?
      status == $xB0 ? (       // Is it a controller event?
        msg2 == modcc ? (      // Is it the right CC?
          modval = msg3;
          outlevel < f1 ? outlevel = f1;
          modlevel = f(modval);
//        slider5 = modlevel;                        // test
        );
      );
    );
    midisend(offset, msg1, msg2, msg3); // pass through
  );    
  
  modstep  = exp( log(modlevel / outlevel) / samplesblock);
  modstep == 1 ? (
    count = 10000000;
   ) : (
    modstep > s1 ? (
      modstep = s1;
     ) : modstep < s2 ? ( 
      modstep = s2;
    );  
    count = 0;
  );  
 
  
@sample
  count < samplesblock ? (
    outlevel *= modstep;
    count += 1;
  );  
  spl0*=outlevel;
  spl1*=outlevel;  
  
  
@gfx 640 400

gfx_r=gfx_g=gfx_b=0; gfx_a=1;
gfx_x=gfx_y=0;
gfx_rectto(gfx_w,gfx_h);

q1 = gfx_w / maxCCvalue;
q2 = gfx_h;


//gfx_line();
gfx_r=gfx_g=gfx_b=1;
gfx_y = 0;
gfx_x = 0;
x = 0;
while (x<=maxCCvalue) (
  a = x*q1;
  b = gfx_h - f(x)*q2;
  gfx_lineto(a, b, 1);
  x = x+1;
);
gfx_y = 0;
gfx_x = modval*q1;
gfx_lineto(gfx_x, gfx_h);
gfx_x = 0;
gfx_y = gfx_h-outlevel*q2;   
gfx_lineto(gfx_w, gfx_y);
mschnell is offline   Reply With Quote
Old 08-27-2016, 01:12 AM   #61
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

As I believe my code now has reached a rather satisfying state, I posted it in a new forum thread.

(Even less CPU time used in that version...)

Thanks for your inspiration !
-Michael

Last edited by mschnell; 08-28-2016 at 10:17 PM.
mschnell is offline   Reply With Quote
Old 08-28-2016, 04:28 PM   #62
Time Waster
Human being with feelings
 
Time Waster's Avatar
 
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
Default

Thanks for the thread Michael, it has been very helpful.

- Mal.
__________________
Mal, aka The Wasters of Time
Mal's JSFX: ReaRack2 Modular Synth
Time Waster is offline   Reply With Quote
Old 08-28-2016, 10:18 PM   #63
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

Same here !
(Please mind that the final version is in the other thread.)
-Michael
mschnell is offline   Reply With Quote
Old 08-29-2016, 06:57 PM   #64
Time Waster
Human being with feelings
 
Time Waster's Avatar
 
Join Date: Aug 2013
Location: Bowral, Australia
Posts: 1,638
Default

I discovered yesterday that I had the interpolation counter reset in the wrong place in the original code that I posted. I worked fine as long as the FX received a MIDI message in every block. However, yesterday I optimised my other modules so that they only send a MIDI message if it is a different value than the previous one. Then Amp module no longer worked properly. I have now fixed this, but if anyone had tried it without sending a message every block, it wouldn't have worked as expected.

My latest code for the simple stepwise Amp module is below:

Code:
// Author: Malcolm Smith ('Time Waster' on REAPER forums).
// License: LGPL - http://www.gnu.org/licenses/lgpl.html

desc:ReaRack - Amplifier

slider1:1<0,127,1{0 Bank Sel M,1 Mod Wheel M,2 Breath M,3,4 Foot P M,5 Porta M,6 Data Entry M,7 Vol M,8 Balance M,9,10 Pan M,11 Expression M,12 Ctrl 1 M,13 Ctrl 2 M,14,15,16 GP Slider 1,17 GP Slider 2,18 GP Slider 3,19 GP Slider 4,20,21,22,23,24,25,26,27,28,29,30,31,32 Bank Sel L,33 Mod Wheel L,34 Breath L,35,36 Foot P L,37 Porta L,38 Data Entry L,39 Vol L,40 Balance L,41,42 Pan L,43 Expression L,44 Ctrl 1 L,45 Ctrl 2 L,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64 Hold P sw,65 Porta sw,66 Sustenuto sw,67 Soft P sw,68 Legato P sw,69 Hold 2 P sw,70 S.Variation,71 S.Timbre,72 S.Release,73 S.Attack,74 S.Brightness,75 S.Ctrl 6,76 S.Ctrl 7,77 S.Ctrl 8,78 S.Ctrl 9,79 S.Ctrl 10,80 GP B.1 sw,81 GP B.2 sw,82 GP B.3 sw,83 GP B.4 sw,84,85,86,87,88,89,90,91 Effects Lv,92 Trem Lv,93 Chorus Lv,94 Celeste Lv,95 Phaser Lv,96 Data B. Inc,97 Data B. Dec,98 NRP L,99 NRP M,100 RP L,101 RP M,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127}>CC Input

@init

modval = 0;
prelevel = 0;

@slider

modcc = slider1;

@block

modcount = min(samplesblock,25);
  
while (midirecv(offset, msg1, msg2, msg3))
(  
  // Extract message type
  status = msg1 & $xF0;    
  // Is it a controller event?
  status == $xB0 ?
  (
    // Is it the right CC?
    msg2 == modcc ?
    (
     modval = msg3;
     modval = 0 ? modlevel = 0: 
     modlevel = 2^((((modval/127)-1)*60)/6);
     modstep = ((modlevel - prelevel)/modcount);
     count = 0;
    );
  );
  // Pass thru
  midisend(offset, msg1, msg2, msg3); 
);     
outlevel = prelevel;
prelevel = modlevel;

@sample
count <= modcount? outlevel +=modstep;
spl0=spl0*outlevel;
spl1=spl1*outlevel;
count += 1;

@gfx 400 150
__________________
Mal, aka The Wasters of Time
Mal's JSFX: ReaRack2 Modular Synth
Time Waster is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 12:18 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.