Go Back   Cockos Incorporated Forums > REAPER Forums > ReaScript, JSFX, REAPER Plug-in Extensions, Developer Forum

Reply
 
Thread Tools Display Modes
Old 11-05-2015, 07:18 PM   #81
derek.john.evans
Human being with feelings
 
derek.john.evans's Avatar
 
Join Date: Feb 2011
Location: Adelaide, South Australia
Posts: 217
Default

Quote:
Originally Posted by mrlimbic View Post
Yes that's it. It is like ducking but for panning instead of volume (pucking?!!)

It seems I could do it as a multichannel effect (like ducking) or via envelope generation and stick the envelope to the pan parameter of another item.
Cool. Yea, I can see a use for that with left/right panned instruments which go mono based on the stereo field of the drum track. I can see a number of ways to implement this, depending on how fancy you want it.

Via the DAW, you would modulate a pan parameter of an effect (eg: volume_pan), and use "audio control signal" which tracks audio coming from channel 4. This channel would be sent over from the drum beat (to channels 3+4) and converted to MS, which means channel 4 would be the drum stereo field.

Via a compressor. A little harder, but, you compress a tracks side (S) in MS mode. The detector input of the compessor again would be the side (S) signal sent from a drum track.

Via code. O, my. A lot of algorithms are running though my head.

Start with a basic pan effect for channels 1+2. Calculate MS for 1+2. Calculate a envelope follower for the side (S) of 3+4. Apply the envelope to the (S) of 1+2. Convert MS back to LR and output to 1+2.

This is a quick implementation. EDIT: Only tested at srate=44100. Im not sure I got the decay calculation correct in relation to srate.

Code:
desc: Pan Ducker (The Pucker)

slider1:0<-1,1>Pan
slider2:10<0,20>Puck Level
slider3:50<0,100>Puck Decay

@init

sc = 6 / log(2);

@slider

sin = sin(slider1 * $pi * 0.5);

pan0 = (1 - sin) * 0.5;
pan1 = (sin + 1) * 0.5;

decay = slider3 / srate;

@sample

spl0 *= pan0; 
spl1 *= pan1;

in0 = spl0 + spl1;
in1 = spl0 - spl1;

in3 += (sqrt(abs(spl2 - spl3)) - in3) * decay;
in1 *= cos(atan(in3 * slider2));

spl0 = (in0 + in1) * 0.5;
spl1 = (in0 - in1) * 0.5;
If you pan a guitar to the left (via the effect), and send the drums to channels 3+4, you will hear the guitar pump to the center when the drums hit off center.

Another idea I have is, track the average angles of 2 stereo tracks using atan2(). Again, use a decay or average over time.

This should get you two angles which you want to separate (push apart). Calculate the desired angles. ie: 45 degrees apart and again, apply a decay to soften the shift.

Then you know how much to push the angles apart via cos/sin rotation. Its all 2D maths. I come from a 3D background, so I see LR as being XY, and therefore vector maths apply.

In theory, you could have an effect with 8 stereo inputs, with a stereo field repulsion of each stereo tracks average angle.

Hope that's not too much babble, but, yea, I see some potential ideas here.
__________________
http://wascal.net/music/
derek.john.evans is offline   Reply With Quote
Old 11-06-2015, 03:57 AM   #82
mrlimbic
Human being with feelings
 
mrlimbic's Avatar
 
Join Date: Nov 2009
Location: UK
Posts: 669
Default

Quote:
Originally Posted by derek.john.evans View Post
Cool. Yea, I can see a use for that with left/right panned instruments which go mono based on the stereo field of the drum track. I can see a number of ways to implement this, depending on how fancy you want it.

Via the DAW, you would modulate a pan parameter of an effect (eg: volume_pan), and use "audio control signal" which tracks audio coming from channel 4. This channel would be sent over from the drum beat (to channels 3+4) and converted to MS, which means channel 4 would be the drum stereo field.

Via a compressor. A little harder, but, you compress a tracks side (S) in MS mode. The detector input of the compessor again would be the side (S) signal sent from a drum track.
Yes in the end I ended up doing pretty the first one. I used M/S encoder and parameter modulation to control pan from only the S channel. It it a little hard to fine tune but seems to work.

To generate a pan envelope might still be a useful thing to do as you can then fine tune more easily if some points are off.

I'm sure I saw someone mention a script that generates a volume envelope from audio levels but can't find it now. You could take the input of that from the M/S encoder to get a stereo width envelope.
__________________
Vordio - Post Production Toolkit
http://vordio.net
mrlimbic is offline   Reply With Quote
Old 11-06-2015, 09:39 AM   #83
mrlimbic
Human being with feelings
 
mrlimbic's Avatar
 
Join Date: Nov 2009
Location: UK
Posts: 669
Default

I've just tried out your Pucker code instead of the parameter modulation method I used yesterday but can't seem to get it to work.

It doesn't seem to pan with the sounds am using.

See screenshot. Am trying to pan the mbira to follow the harp wherever it goes.

I render all four channels just to check the send from harp was working and so could view the 'panned' output.
Attached Images
File Type: jpg Screen Shot 2015-11-06 at 16.36.18.jpg (50.8 KB, 547 views)
__________________
Vordio - Post Production Toolkit
http://vordio.net
mrlimbic is offline   Reply With Quote
Old 11-06-2015, 08:02 PM   #84
derek.john.evans
Human being with feelings
 
derek.john.evans's Avatar
 
Join Date: Feb 2011
Location: Adelaide, South Australia
Posts: 217
Default

Quote:
Originally Posted by mrlimbic View Post
I've just tried out your Pucker code instead of the parameter modulation method I used yesterday but can't seem to get it to work.

It doesn't seem to pan with the sounds am using.

See screenshot. Am trying to pan the mbira to follow the harp wherever it goes.

I render all four channels just to check the send from harp was working and so could view the 'panned' output.
I think it might be because you are expecting that code to behave like the modulated pan you have been using. The code currently decreases the side (S) of 1+2 as the side (S) of 3+4 increases.

Therefore, you pan a mono signal to the left or right, and then it will pan to the center as 3+4 becomes more stereo.

The issue is, side (S) doesn't contain LR information. Its the combination of MS which gives you that info.

So, here is where you need to think up some coding ideas.

One idea, is to just implement a modulation style effect. So, have two pans for the effect. ie: A start pan, and a duck pan. And then modulate between the two based on the level of the side of 3+4.

Or, you can forget the side (S) idea, and calculate the running levels of LR for 3+4. Then, apply those levels to 1+2 in reverse. ie: So, as 3+4 moves left, 1+2 moves right.

The complexity of this depends on if you want to handle pre-panned 1+2 signals. ie: Imagine two stereo drum tracks and what the algorithm would be to mix those together in such a way that LR signals are separated as much as possible.

So, many ideas. Sounds like you are getting closer to what you want to code.
__________________
http://wascal.net/music/
derek.john.evans is offline   Reply With Quote
Old 06-03-2016, 06:15 AM   #85
SaschArt
Human being with feelings
 
SaschArt's Avatar
 
Join Date: Aug 2013
Posts: 236
Default

Array for scales:

Code:
slider1:5<0,11,1{Chromatic,Dorian,Harmonic Minor,Locrian,Lydian,Major,Melodic Minor,Mixolydian,Natural Minor,Pentatonic Major,Pentatonic Minor,Phrygian,Whole Tone}>Scale

@init
arr_scale = 500;  //init the array
arr_scale[12]="111111111111";    //Chromatic
arr_scale[13]="101101010110";    //Dorian
arr_scale[14]="101101011001";    //Harmonic Minor
arr_scale[15]="110110011010";    //Locrian
arr_scale[16]="101010110101";    //Lydian
arr_scale[17]="101011010101";    //Major
arr_scale[18]="101101010101";    //Melodic Minor
arr_scale[19]="101011010110";    //Mixolydian
arr_scale[20]="101101011010";    //Natural Minor
arr_scale[21]="101010010100";    //Pentatonic Major
arr_scale[22]="100101010010";    //Pentatonic Minor
arr_scale[23]="110101011010";    //Phrygian
arr_scale[24]="101010101010";    //Whole Tone
scale=-1;

@slider

scale != slider1 ? (
  scale = slider1; 
  scale_size = 0;
  i=0;
  while (i<12) (
    strcmp(strcpy_substr(#,arr_scale[scale+12],i,1), "1")==0 ? (
      arr_scale[scale_size]=i;
      scale_size+=1;
    );
    i+=1;
  );
);
arr_scale will contain scores from each scale.
SaschArt is offline   Reply With Quote
Old 06-03-2016, 06:28 AM   #86
SaschArt
Human being with feelings
 
SaschArt's Avatar
 
Join Date: Aug 2013
Posts: 236
Default

Split string in array:
Code:
function splitString(str,sep,arr) 
local(i,pos) (
  i=pos=0;
  #reg="%S";
  #reg+=sep;
  #reg+="*";
  string_pos+=1;
  len=strlen(str);
  while (match(#reg,strcpy_from(#,str,pos),string_pos) && i<555) (
    arr[i]=string_pos;
    pos+=strlen(string_pos)+1;
    i+=1;
    string_pos+=1;
  );
  pos>0 ? arr[i]=strcpy_from(string_pos,str,pos);
);

arr1=0;
splitString("C;C#;D;D#;E;F;F#;G;G#;A;A#;B",";",arr1);
SaschArt is offline   Reply With Quote
Old 08-22-2016, 12:11 PM   #87
teatime
Human being with feelings
 
teatime's Avatar
 
Join Date: Aug 2016
Location: South Africa
Posts: 44
Default Quick Sine

This is a dirt-cheap quadratic approximation of a sine:

B = 4/$PI;
C = -4/($PI*$PI);

function quicksine(x) local(y)
(
y = B*x + C*x*abs(x);
y = 0.225 * (y * abs(y) - y) + y;
y;
);

Haven't listened to it, but it does pretty well as an LFO.

NB: Only valid between -PI and PI. Discovered here: http://forum.devmaster.net/t/fast-an...ne-cosine/9648

I recommend following the link, there are varying levels of accuracy to be had with this approximation, depending on what you're optimising for.
teatime is offline   Reply With Quote
Old 09-07-2016, 04:55 AM   #88
geraintluff
Human being with feelings
 
geraintluff's Avatar
 
Join Date: Nov 2009
Location: mostly inside my own head
Posts: 346
Default FFT for large sizes

I wanted to use (I)FFT to generate samples of size 65536 and above, so I wrote a function to perform larger FFTs.

As much work as possible is still done by the built-in functions - this just Cooley-Tukey factorises the large FFT into two smaller ones.

It performs the permutations (part of the algorithm), and needs some working memory (for any sensible FFT size, 256 slots is all it needs - see fft_big()'s implementation for details). I've thought about a version that doesn't need this working memory, but it may or may not be slower.

Usage:

Code:
fft_big(buffer, 65536, working_mem);
buffer[1] = 1;
ifft_big(buffer, 65536, working_mem);
Code:

Code:
// working_space must be 2*sizeB big
function fft_ab(block, sizeA, sizeB, working_space)
		local(N, i, j, twiddle_amount, twiddle_r, twiddle_i, Nbits, shiftleftbits, shiftrightbits, bitmask, index1, source_index, target_index, bitmask, tmp_r, tmp_i)
		(
	N = sizeA*sizeB;
	i = 0;
	// Perform the stepwise FFTs
	while (i < sizeA) (
		j = 0;
		// Copy to working area
		while (j < sizeB) (
			working_space[j*2] = block[(i + j*sizeA)*2];
			working_space[j*2 + 1] = block[(i + j*sizeA)*2 + 1];
			j += 1;
		);
		fft(working_space, sizeB);
		fft_permute(working_space, sizeB);
		// Copy back, with twiddle factors
		j = 0;
		while (j < sizeB) (
			twiddle_amount = -i*j/N;
			twiddle_r = cos(twiddle_amount*2*$pi);
			twiddle_i = sin(twiddle_amount*2*$pi);
		
			block[(i + j*sizeA)*2] = working_space[j*2]*twiddle_r - working_space[j*2 + 1]*twiddle_i;
			block[(i + j*sizeA)*2 + 1] = working_space[j*2]*twiddle_i + working_space[j*2 + 1]*twiddle_r;
			j += 1;
		);

		i += 1;
	);
	
	// Perform the in-place FFTs
	j = 0;
	while (j < sizeB) (
		fft(block + j*sizeA*2, sizeA);
		fft_permute(block + j*sizeA*2, sizeA);
		j += 1;
	);
	
	// Permute
	Nbits = 1;
	while ((1<<Nbits) < N) (
		Nbits += 1;
	);
	shiftleftbits = 1; // This is the shift required to find the source index for a given target
	bitmask = N - 1;
	while ((1<<shiftleftbits) < sizeA) (
		shiftleftbits += 1;
	);
	shiftrightbits = Nbits - shiftleftbits;
	
	index1 = 1; // Source index
	while (index1 < N) (
		target_index = index1;
		while (
			target_index = ((target_index<<shiftleftbits)&bitmask) + (target_index>>shiftrightbits);
			target_index > index1;
		);
		target_index == index1 ? ( // This index is the shortest in its permutation group
			tmp_r = block[target_index*2];
			tmp_i = block[target_index*2 + 1];
			source_index = ((target_index<<shiftleftbits)&bitmask) + (target_index>>shiftrightbits);
			while (source_index != index1) (
				block[target_index*2] = block[source_index*2];
				block[target_index*2 + 1] = block[source_index*2 + 1];
				target_index = source_index;
				source_index = ((target_index<<shiftleftbits)&bitmask) + (target_index>>shiftrightbits);
			);
			block[target_index*2] = tmp_r;
			block[target_index*2 + 1] = tmp_i;
		);
		index1 += 1;
	);	
);

// working_space needs to be 256 big (128 complex numbers), unless you're doing FFTs of more than 4194304 (2^22)
function fft_big(block, N, working_space) local(sizeB) (
	N > 32768 ? (
		sizeB = 1<<7;
		(N/sizeB) > 32768 ? sizeB = 1<<11;
		(N/sizeB) > 32768 ? sizeB = 1<<13;
		fft_ab(block, N/sizeB, sizeB, working_space);
	) : (
		fft(block, N);
		fft_permute(block, N);
	);
);

// working_space must be 2*sizeB big
function ifft_ab(block, sizeA, sizeB, working_space)
		local(N, i, j, twiddle_amount, twiddle_r, twiddle_i, Nbits, shiftleftbits, shiftrightbits, bitmask, index1, source_index, target_index, bitmask, tmp_r, tmp_i)
		(
	N = sizeA*sizeB;

	// Permute
	Nbits = 1;
	while ((1<<Nbits) < N) (
		Nbits += 1;
	);
	shiftleftbits = 1; // This is the shift required to find the source index for a given target
	bitmask = N - 1;
	while ((1<<shiftleftbits) < sizeB) ( // NOTE: the FFT uses sizeA, the IFFT uses sizeB (inverse shift)
		shiftleftbits += 1;
	);
	shiftrightbits = Nbits - shiftleftbits;
	
	index1 = 1; // Source index
	while (index1 < N) (
		target_index = index1;
		while (
			target_index = ((target_index<<shiftleftbits)&bitmask) + (target_index>>shiftrightbits);
			target_index > index1;
		);
		target_index == index1 ? ( // This index is the shortest in its permutation group
			tmp_r = block[target_index*2];
			tmp_i = block[target_index*2 + 1];
			source_index = ((target_index<<shiftleftbits)&bitmask) + (target_index>>shiftrightbits);
			while (source_index != index1) (
				block[target_index*2] = block[source_index*2];
				block[target_index*2 + 1] = block[source_index*2 + 1];
				target_index = source_index;
				source_index = ((target_index<<shiftleftbits)&bitmask) + (target_index>>shiftrightbits);
			);
			block[target_index*2] = tmp_r;
			block[target_index*2 + 1] = tmp_i;
		);
		index1 += 1;
	);

	// Perform the in-place IFFTs
	j = 0;
	while (j < sizeB) (
		fft_ipermute(block + j*sizeA*2, sizeA);
		ifft(block + j*sizeA*2, sizeA);
		j += 1;
	);

	i = 0;
	// Perform the stepwise IFFTs
	while (i < sizeA) (
		j = 0;
		// Copy to working area, with anti-twiddle factors
		while (j < sizeB) (
			twiddle_amount = i*j/N;
			twiddle_r = cos(twiddle_amount*2*$pi);
			twiddle_i = sin(twiddle_amount*2*$pi);
			
			working_space[j*2] = block[(i + j*sizeA)*2]*twiddle_r - block[(i + j*sizeA)*2 + 1]*twiddle_i;
			working_space[j*2 + 1] = block[(i + j*sizeA)*2]*twiddle_i + block[(i + j*sizeA)*2 + 1]*twiddle_r;
			j += 1;
		);
		fft_ipermute(working_space, sizeB);
		ifft(working_space, sizeB);
		j = 0;
		while (j < sizeB) (
			block[(i + j*sizeA)*2] = working_space[j*2];
			block[(i + j*sizeA)*2 + 1] = working_space[j*2 + 1];
			j += 1;
		);

		i += 1;
	); 
);

function ifft_big(block, N, working_space) local(sizeB) (
	N > 32768 ? (
		sizeB = 1<<7;
		(N/sizeB) > 32768 ? sizeB = 1<<11;
		(N/sizeB) > 32768 ? sizeB = 1<<13;
		ifft_ab(block, N/sizeB, sizeB, working_space);
	) : (
		fft_ipermute(block, N);
		ifft(block, N);
	);
);
The fft_ab() and ifft_ab() functions let you specify the split, but in my speed tests there wasn't much difference (about 3%), and (x, 128) was a good choice. You shouldn't need to use the *_ab() functions, just *_big().
geraintluff is offline   Reply With Quote
Old 10-15-2016, 07:10 PM   #89
chronocepter
Human being with feelings
 
chronocepter's Avatar
 
Join Date: Apr 2010
Posts: 232
Default

Hey, not sure if in the right place... but..
JS:Osciloscope maximum length is too short.
I put some more here:
g_maxlen_ms=10000;

and it becomes way more useful. I do not know how to adjust the mousewhell
it backs to "2" seconds.

Just requesting this 10 seconds become a default feature for good.
__________________
"Another pointless experiment in synthetic stupidity." - Piz
chronocepter 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 02:31 AM.


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