


11052015, 07:18 PM

#81

Human being with feelings
Join Date: Feb 2011
Location: Adelaide, South Australia
Posts: 217

Quote:
Originally Posted by mrlimbic
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.



11062015, 03:57 AM

#82

Human being with feelings
Join Date: Nov 2009
Location: UK
Posts: 557

Quote:
Originally Posted by derek.john.evans
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.



11062015, 09:39 AM

#83

Human being with feelings
Join Date: Nov 2009
Location: UK
Posts: 557

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.



11062015, 08:02 PM

#84

Human being with feelings
Join Date: Feb 2011
Location: Adelaide, South Australia
Posts: 217

Quote:
Originally Posted by mrlimbic
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 prepanned 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.



06032016, 06:15 AM

#85

Human being with feelings
Join Date: Aug 2013
Posts: 188

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.



06032016, 06:28 AM

#86

Human being with feelings
Join Date: Aug 2013
Posts: 188

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);



08222016, 12:11 PM

#87

Human being with feelings
Join Date: Aug 2016
Location: South Africa
Posts: 41

Quick Sine
This is a dirtcheap 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/fastan...necosine/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.



09072016, 04:55 AM

#88

Human being with feelings
Join Date: Nov 2009
Location: mostly inside my own head
Posts: 279

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 builtin functions  this just CooleyTukey 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 inplace 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 inplace 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 antitwiddle 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().



10152016, 07:10 PM

#89

Human being with feelings
Join Date: Apr 2010
Posts: 223

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.



Thread Tools 

Display Modes 
Linear Mode

Posting Rules

You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off



All times are GMT 7. The time now is 05:18 PM.
