version: 2.0.3 desc:ReaRack2 - Oscillator-Additive - Modded noindex: true author: Malcolm Smith Copyright(C) 2017 and later, Malcolm Smith (username 'Time Waster' on REAPER forums). License: LGPL - http://www.gnu.org/licenses/lgpl.html about: Audio oscillator module for the ReaRack modular JSFX synth system. No aliasing. changelog: Version 2.0.3 (08/03/2019) Fixed the FM function. Version 2.0.2 (18/02/2018) Added pitch bend input for pitch change. Added pitch modulation depth slider. Version 2.0.1 (10/02/2018) Improved accuracy of input CC decoding. Version 2.0.0 (10/12/2017) Initial version 2 release. slider1:smodp=0<0,2,1{Pitch,Pulse Width,Filter Cutoff (highest harmonic)}>Modulation Target slider2:sccIn=0<0,64,1{Off,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,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}>CC Input slider3:mrange=1<0,1,0.001>Modulation Range //slider5:0<0,128,1{MIDI note,C0,C#0,D0,D#0,E0,F0,F#0,G0,G#0,A0,A#0,B0,C1,C#1,D1,D#1,E1,F1,F#1,G1,G#1,A1,A#1,B1,C2,C#2,D2,D#2,E2,F2,F#2,G2,G#2,A2,A#2,B2,C3,C#3,D3,D#3,E3,F3,F#3,G3,G#3,A3,A#3,B3,C4,C#4,D4,D#4,E4,F4,F#4,G4,G#4,A4,A#4,B4,C5,C#5,D5,D#5,E5,F5,F#5,G5,G#5,A5,A#5,B5,C6,C#6,D6,D#6,E6,F6,F#6,G6,G#6,A6,A#6,B6,C7,C#7,D7,D#7,E7,F7,F#7,G7,G#7,A7,A#7,B7,C8,C#8,D8,D#8,E8,F8,F#8,G8,G#8,A8,A#8,B8,C9,C#9,D9,D#9,E9,F9,F#9,G9,G#9,A9,A#9,B9,C10,C#10,D10,D#10,E10,F10,F#10,G10}> Pitch slider5:spitch=0<0,128,1{MIDI note,C-1,C#-1,D-1,D#-1,E-1,F-1,F#-1,G-1,G#-1,A-1,A#-1,B-1,C0,C#0,D0,D#0,E0,F0,F#0,G0,G#0,A0,A#0,B0,C1,C#1,D1,D#1,E1,F1,F#1,G1,G#1,A1,A#1,B1,C2,C#2,D2,D#2,E2,F2,F#2,G2,G#2,A2,A#2,B2,C3,C#3,D3,D#3,E3,F3,F#3,G3,G#3,A3,A#3,B3,C4,C#4,D4,D#4,E4,F4,F#4,G4,G#4,A4,A#4,B4,C5,C#5,D5,D#5,E5,F5,F#5,G5,G#5,A5,A#5,B5,C6,C#6,D6,D#6,E6,F6,F#6,G6,G#6,A6,A#6,B6,C7,C#7,D7,D#7,E7,F7,F#7,G7,G#7,A7,A#7,B7,C8,C#8,D8,D#8,E8,F8,F#8,G8,G#8,A8,A#8,B8,C9,C#9,D9,D#9,E9,F9,F#9,G9}>Pitch //slider5:0<0,128,1{MIDI note,C-2,C#-2,D-2,D#-2,E-2,F-2,F#-2,G-2,G#-2,A-2,A#-2,B-2,C-1,C#-1,D-1,D#-1,E-1,F-1,F#-1,G-1,G#-1,A-1,A#-1,B-1,C0,C#0,D0,D#0,E0,F0,F#0,G0,G#0,A0,A#0,B0,C1,C#1,D1,D#1,E1,F1,F#1,G1,G#1,A1,A#1,B1,C2,C#2,D2,D#2,E2,F2,F#2,G2,G#2,A2,A#2,B2,C3,C#3,D3,D#3,E3,F3,F#3,G3,G#3,A3,A#3,B3,C4,C#4,D4,D#4,E4,F4,F#4,G4,G#4,A4,A#4,B4,C5,C#5,D5,D#5,E5,F5,F#5,G5,G#5,A5,A#5,B5,C6,C#6,D6,D#6,E6,F6,F#6,G6,G#6,A6,A#6,B6,C7,C#7,D7,D#7,E7,F7,F#7,G7,G#7,A7,A#7,B7,C8,C#8,D8,D#8,E8,F8,F#8,G8}>Pitch slider6:sharm=0<1,16,1{1 (fundamental),2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Lowest Harmonic slider7:soct=0<-4,4,1>Tune (octave) slider8:ssemi=0<-12,12,1>Tune (semitone) slider9:scent=0<-100,100,1>Fine Tune (cents) slider10:sport=0<0,3>Portamento (sec) slider11:smodetune=0<-1,1,0.001>Pitch Modulation Centre slider12:spdepth=1<0,4,0.01>Master Pitch Mod. Range (octaves) // Wave Shape Controls slider14:sshape=0<0,2,1{Sawtooth,Triangle,Pulse}>Wave Shape slider15:spulsew=0.5<0.01,0.99>Pulse Width slider17:smaxh=128<1,128,0.001> Filter Cutoff (highest harmonic) slider18:ssweepmode=0<0,1,1{Step,Continuous}>Filter Sweep Mode slider20:sreso=0<0,5,.001>Resonance slider22:smodaudio=0<0,3,1{Off,Frequency Modulation,Ring Modulation,Amplitude Modulation}>Audio Modulation //slider23:svoltarget=1<0,10,.001>Overdrive slider24:soutput=1<0,2,.001>Output Level slider26:sclass=0<0,3,1{None,Master,Slave}>Class slider27:sgroup=0<0,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Group // Hidden sliders slider41:pitchccmsb=-1<-1,128,1>- slider42:pwccmsb=-1<-1,128,1>- slider43:harmccmsb=-1<-1,128,1>- slider47:pitchm=1<0,1,0.001>- slider48:pwm=1<0,1,0.001>- slider49:harmm=1<0,1,0.001>- options:gmem=ReaRack @init fxn = 60; pitchcclsb = pitchccmsb+32; pwcclsb = pwccmsb+32; harmcclsb = harmccmsb+32; tau = $pi*2.0; freq = 440; adj = tau*freq/srate; blkadj = tau*440/srate; modadj = 1; pitchadj = 1; volcount = ceil(srate/100); @slider slidermove = 1; @block sclass == 2 ? slidermove = gmem[group+fxn+21]; slidermove == 1 ? ( // Update modulation input sliders to previously set values on target selection. smodp == 0 && ccInnum != 0 ? ( //Pitch sccIn = pitchccmsb <= 31 ? pitchccmsb+1:pitchccmsb - 31; mrange = pitchm; ); smodp == 1 && ccInnum != 1 ? ( //Filter 1 sccIn = pwccmsb <= 31 ? pwccmsb+1:pwccmsb - 31; mrange = pwm; ); smodp == 2 && ccInnum != 2 ? ( //Filter 2 sccIn = harmccmsb <= 31 ? harmccmsb+1:harmccmsb - 31; mrange = harmm; ); /////////////////////////Get slider values////////////////////////// smodp == 0 ? //Pitch ( pitchccmsb = sccIn <= 32 ? sccIn-1:sccIn+31; pitchcclsb = pitchccmsb+32; pitchm = mrange; ccInnum = 0; ); smodp == 1 ? //Pulse Width ( pwccmsb = sccIn <= 32 ? sccIn-1:sccIn+31; pwcclsb = pwccmsb+32; pwm = mrange; ccInnum = 1; ); smodp == 2 ?//Filter ( harmccmsb = sccIn <= 32 ? sccIn-1:sccIn+31; harmcclsb = harmccmsb+32; harmm = mrange; ccInnum = 2; ); //Store slider values group = sgroup*1000; sclass == 1 ? ( gmem[group+fxn+0] = soutput; gmem[group+fxn+1] = sshape; gmem[group+fxn+2] = spulsew; gmem[group+fxn+3] = spwmodlev; gmem[group+fxn+4] = smaxh; gmem[group+fxn+5] = sharmodlev; gmem[group+fxn+6] = sharm; gmem[group+fxn+7] = soct; gmem[group+fxn+8] = ssemi; gmem[group+fxn+9] = scent; gmem[group+fxn+10] = sport; gmem[group+fxn+11] = smodetune; gmem[group+fxn+12] = smodlev; gmem[group+fxn+13] = smodaudio; gmem[group+fxn+14] = ssweepmode; gmem[group+fxn+15] = pitchm; gmem[group+fxn+16] = pwm; gmem[group+fxn+17] = harmm; gmem[group+fxn+18] = smodp; gmem[group+fxn+19] = sccIn; gmem[group+fxn+20] = mrange; gmem[group+fxn+21] = slidermove; gmem[group+fxn+22] = spitch; gmem[group+fxn+23] = sreso; gmem[group+fxn+24] = spdepth; ); // Retrieve slider values sclass == 2 ? ( soutput = gmem[group+fxn+0]; sshape = gmem[group+fxn+1]; spulsew = gmem[group+fxn+2]; spwmodlev = gmem[group+fxn+3]; smaxh = gmem[group+fxn+4]; sharmodlev = gmem[group+fxn+5]; sharm = gmem[group+fxn+6]; soct = gmem[group+fxn+7]; ssemi = gmem[group+fxn+8]; scent = gmem[group+fxn+9]; sport = gmem[group+fxn+10]; smodetune = gmem[group+fxn+11]; smodlev = gmem[group+fxn+12]; smodaudio = gmem[group+fxn+13]; ssweepmode = gmem[group+fxn+14]; pitchm = gmem[group+fxn+15]; pwm = gmem[group+fxn+16]; harmm = gmem[group+fxn+17]; smodp = gmem[group+fxn+18]; sccIn = gmem[group+fxn+19]; mrange = gmem[group+fxn+20]; spitch = gmem[group+fxn+22]; sreso = gmem[group+fxn+23]; spdepth = gmem[group+fxn+24]; ); pitch = spitch-1; voltarget = soutput*0.5; shape = sshape; pulsew = spulsew*tau; pwmodlev = pwm*tau; maxh = floor(smaxh); lasth = smaxh-maxh; harmodlev = max(1,harmm*128); sweepmode = ssweepmode; harm = sharm+1; detune = 2^(soct + ssemi/12 + scent/1200); portaval = sport; modcentre = smodetune; modlev = pitchm; modaudio = smodaudio; volstep = (voltarget-prevol)/volcount; volstepcount = volcount; reso = 1+sreso; // Portamento portastep = floor(max(1,((srate/samplesblock)*portaval))); slidermove = 0; ); // end if slidermove /////////////////////////Get MIDI inputs///////////////// // MIDI keyboard pitch control while (midirecv(offset, msg1, msg2, msg3)) ( // Extract message type and channel status = msg1 & $xF0; // Is it a note on event? status == $x90 && pitch == -1 ? ( // Get note and velocity note = msg2 & $x7F; //velocity = msg3; // Note on freq = 2^((note-69)/12)*440*harm*detune; blkadjtarget = tau*freq/srate; //End of Get note and velocity ); ////////////////////////Get modulation CC messages//////// // Extract message type and channel status == $xB0 ? ( // Pitch Modulation // Is it the right CC? msg2 == pitchccmsb || msg == pitchcclsb ? ( msg2 == pitchccmsb ? modvalmsb = msg3; msg2 == pitchcclsb ? modvallsb = msg3; modval = ((modvalmsb/(127.9921875/2))+(modvallsb/8192)) - (1+modcentre); modlevel = modval * modlev; modadj = 2^(modlevel*spdepth); ); // Pulse Width Modulation // Is it the right CC? msg2 == pwccmsb || msg == pwcclsb ? ( msg2 == pwccmsb ? pwvalmsb = msg3; msg2 == pwcclsb ? pwvallsb = msg3; pwval = (pwvalmsb/127.9921875)+(pwvallsb/16384); pw = pulsew+pwval * (pwmodlev - pulsew); ); // Filter Modulation // Is it the right CC? msg2 == widthccmsb || msg == widthcclsb ? ( msg2 == harmccmsb ? harmvalmsb = msg3; msg2 == harmcclsb ? harmvallsb = msg3; harmval = (harmvalmsb/127.9921875)+(harmvallsb/16384); maxhout1 = smaxh + (harmval*(harmodlev-maxh)); maxhout = floor(maxhout1); lasthout = maxhout1 - maxhout; //lasthout2 = ((maxhout1-1) - 2*(floor((maxhout-1)/2)))/2; ); ); status == $xE0 ? ( // Pitch Wheel pitchwheel_val = (msg2/8192)+(msg3/(64))-1; pitchadj = 2^(pitchwheel_val*spdepth); ); // Pass through midisend(offset, msg1, msg2, msg3); ); // Adjust for portamento if required adjstep = (blkadjtarget - blkadj)/portastep; blkadj != blkadjtarget ? ( blkadj += adjstep); /////////////////////////Slider pitch control////////////// pitch > -1 ? ( freq = 2^((pitch-69)/12)*440*harm*detune; blkadj = tau*freq/srate; ); /////////Zero modulation values if no input////////////// pitchccmsb == -1 ? modadj = 1; pwccmsb == -1 ? pw = pulsew; harmccmsb == -1 ? ( maxhout = maxh; lasthout = lasth; ); @sample shape == 1 ? ( cutoff = min(floor(maxhout),floor(srate/(freq*4))); ):( cutoff = min(floor(maxhout),floor(srate/(freq+freq))); ); /////////////////////////Volume ramping///////////////////////// vol != voltarget ? ( volstepcount >=0 ? ( vol += volstep; volstepcount -= 1; ):( vol = voltarget; // Just to be sure! ); ); /////////////////////////////FM///////////////////////////////// inlev = (spl0+spl1)/2; modaudio == 1 ? adj = blkadj*(1+inlev)*10: adj = blkadj; ////////////////////////////Define waveforms//////////////////// shape == 0 ? ( // Sawtooth s0 = 0; s1 = sin(pos); sx = 2*cos(pos); sample = s1; wharm = 2; while(wharm < cutoff)( sn = sx*s1 - s0; sample += sn/wharm; s0 = s1; s1 = sn; wharm +=1; ); sweepmode == 1 ? ( sample += (1+((1-lasthout)*(reso-1)))*sin(wharm*pos)/wharm; sample += reso*lasthout*(sin((wharm+1)*pos)/(wharm+1)); ):( sample += reso*sin(wharm*pos)/wharm; ); ):( shape == 1 ? ( // Triangle s0 = 0; s1 = sin(pos); sx = 2*cos(pos); sample = s1; wharm = 1; sgn = -1; oddw2 = 3*3; while(wharm < cutoff-1)( sn = sx*s1 - s0; // sin(2x) s0 = s1; s1 = sn; sn = sx*s1 - s0; // sin(3x) sample += sgn*sn/oddw2; s0 = s1; s1 = sn; sgn *= -1; wharm +=1; oddw2 += 8*wharm; ); sweepmode == 1 ? ( sample += (1+((1-lasthout)*(reso-1)))*((-1)^wharm*(sin((2*wharm+1)*pos)/(2*wharm+1)^2)); wharm += 1; sample += reso*lasthout*((-1)^wharm*(sin((2*wharm+1)*pos)/(2*wharm+1)^2)); ):( sample += reso*((-1)^wharm*(sin((2*wharm+1)*pos)/(2*wharm+1)^2)); ); sample *= 16/$pi^2; ):( shape == 2 ? ( // Pulse s0 = 0; s1 = sin(pw/2); sx = 2*cos(pw/2); c0 = 1; c1 = cos(pos); cx = 2*cos(pos); sample = 2*s1*c1; wharm = 2; while(wharm <= cutoff)( sn = sx*s1 - s0; cn = cx*c1 - c0; sample += (2/wharm)*sn*cn; s0 = s1; s1 = sn; c0 = c1; c1 = cn; wharm +=1; ); sweepmode == 1 ? ( sample += (1+((1-lasthout)*(reso-1)))*(2/(wharm*$pi))*sin((wharm*pw)/2)*cos(wharm*pos); sample += reso*lasthout*(2/((wharm+1)*$pi))*sin(((wharm+1)*pw)/2)*cos((wharm+1)*pos); ):( sample += reso*(2/(wharm*$pi))*sin((wharm*pw)/2)*cos(wharm*pos); ); sample += pw/tau - 0.5; ); ); ); sample *= vol; ///////////////////////////////Audio output///////////////////////////// modaudio == 0 ? ( //Unmodulated waveform spl0 = sample+spl0; spl1 = sample+spl1; ) : ( modaudio == 2 ? ( //Ring Modulation spl0 = (sample*spl0); spl1 = (sample*spl1); ) : ( modaudio == 3 ? ( //Amplitude Modulation spl0 = ((0.5+(sample/2))*spl0); spl1 = ((0.5+(sample/2))*spl1); ) : ( //Frequency Modulation spl0 = sample; spl1 = sample; ); ); ); //////////Buffer results for graphics//////// looplen = srate/8; bufflen = floor(looplen); bpos<=bufflen ? ( bpos[0] = pos; modaudio == 0 ? (bpos+bufflen)[0] = sample: (bpos+bufflen)[0] = (spl0+spl1)/2; ); buffcount+=1; buffcount > looplen ? ( buffcount-=looplen; ); bpos=floor(buffcount); ////////Increment phase position/////////// pos += adj*modadj*pitchadj; (pos >= tau) ? pos -= tau; prevol = vol; /////////////////////////////////graphics////////////////////////////////////// @gfx 400 140 xmin = 25; xrng = 150; sclx = xrng/tau; xmax = xmin+xrng; ymin = 25; yrng = 100; ymax = ymin+yrng; scly = yrng/2; mxmin = xmax+200; mxrng = 100; myrng = 6; gfx_r = 0; gfx_g = 1; gfx_b = 0; gfx_a = 1; gpos = 1; gfx_x = xmin; gfx_y = ymin; while (gpos <= bufflen)( gfx_x = xmin+sclx*gpos[0]; gfx_y = ymin+scly+scly*(gpos+bufflen)[0]; gfx_y <= ymax && gfx_y >= ymin ? gfx_setpixel(0,1,0); gpos+=1; ); // Text gfx_r=1; gfx_b=1; gfx_g=1; gfx_a=0.7; gfx_x=67; gfx_y=8; gfx_drawstr("Waveform"); gfx_x=mxmin-45; gfx_y=8; gfx_drawstr("Modulation"); gfx_x=mxmin-110; gfx_y=23; gfx_drawstr(" Target CC Level"); gfx_x=25; gfx_y=ymax+10; gfx_lineto(gfx_x, gfx_y,0); gfx_drawstr("Base Frequency = "); gfx_drawnumber(freq,0); gfx_drawstr(" Hz"); gfx_x=25; gfx_y=ymax+40; gfx_drawstr("No. of Harmonics = "); gfx_drawnumber(cutoff,0); pitchccmsb > -1 ? ( gfx_x=25; gfx_y=ymax+25; gfx_lineto(gfx_x, gfx_y,0); gfx_drawstr("Modulated Freq. = "); gfx_drawnumber(freq+freq*modlevel,0); gfx_drawstr(" Hz"); ); gfx_r = 1; gfx_g = 0; gfx_b = 0; gfx_a = 0.5; gfx_x = xmin; gfx_y = ymin; gfx_lineto(xmin+(sclx*tau),ymin); gfx_x = xmin; gfx_y = ymin+scly; gfx_lineto(25+(sclx*tau),ymin+scly); gfx_x = xmin; gfx_y = ymin+scly+scly; gfx_lineto(xmin+(sclx*tau),ymin+scly+scly); gfx_a =1; gfx_x = xmin-12; gfx_y = ymin-5; gfx_drawstr("1"); gfx_x = xmin-12; gfx_y = (ymin-5)+scly; gfx_drawstr("0"); gfx_x = xmin-20; gfx_y = (ymin-5)+scly+scly; gfx_drawstr("-1"); // Modulation Indicators // Pitch smodp == 0 ? gfx_a = 1: gfx_a = 0.5; gfx_x = mxmin-150; gfx_y = ymin+15; //gfx_drawstr(" Pitch"); gfx_drawstr(" Pitch "); pitchccmsb < 0 ? gfx_drawstr("--"): pitchccmsb < 10 ? (gfx_drawstr("0"); gfx_drawnumber(pitchccmsb,0)): gfx_drawnumber(pitchccmsb,0); gfx_x = mxmin; gfx_y = ymin+15; gfx_lineto(gfx_x+mxrng,gfx_y); gfx_lineto(gfx_x,gfx_y+myrng); gfx_lineto(gfx_x-mxrng,gfx_y); gfx_lineto(gfx_x,gfx_y-myrng); gfx_r = 0; gfx_g = 1; gfx_x = mxmin+(1+modcentre)*(mxrng/2); gfx_y = ymin+15+5; gfx_lineto(gfx_x,gfx_y-4); gfx_rectto(gfx_x+(modlevel*(mxrng/2)),gfx_y+4); // Pulse Width gfx_r = 1; gfx_g = 0; smodp == 1 ? gfx_a = 1: gfx_a = 0.5; gfx_x = mxmin-150; gfx_y = ymin+35; gfx_drawstr(" Pulse Width "); pwccmsb < 0 ? gfx_drawstr("--"): pwccmsb < 10 ? (gfx_drawstr("0"); gfx_drawnumber(pwccmsb,0)): gfx_drawnumber(pwccmsb,0); gfx_x = mxmin; gfx_y = ymin+35; gfx_lineto(gfx_x+mxrng,gfx_y); gfx_lineto(gfx_x,gfx_y+myrng); gfx_lineto(gfx_x-mxrng,gfx_y); gfx_lineto(gfx_x,gfx_y-myrng); gfx_r = 0; gfx_g = 1; gfx_x = mxmin+(pulsew/tau)*mxrng; gfx_y = ymin+35+5; gfx_lineto(gfx_x,gfx_y-4); gfx_rectto(mxmin+(pw/tau)*mxrng,gfx_y+4); // Filter Cutoff gfx_r = 1; gfx_g = 0; smodp == 2 ? gfx_a = 1: gfx_a = 0.5; gfx_x = mxmin-150; gfx_y = ymin+55; gfx_drawstr("Filter Cutoff "); harmccmsb < 0 ? gfx_drawstr("--"): harmccmsb < 10 ? (gfx_drawstr("0"); gfx_drawnumber(harmccmsb,0)): gfx_drawnumber(harmccmsb,0); gfx_x = mxmin; gfx_y = ymin+55; gfx_lineto(gfx_x+mxrng,gfx_y); gfx_lineto(gfx_x,gfx_y+myrng); gfx_lineto(gfx_x-mxrng,gfx_y); gfx_lineto(gfx_x,gfx_y-myrng); gfx_r = 0; gfx_g = 1; gfx_x = mxmin+(smaxh/128)*mxrng; gfx_y = ymin+55+5; gfx_lineto(gfx_x,gfx_y-4); gfx_rectto(mxmin+(maxhout1/128)*mxrng,gfx_y+4);