-psycledelics- (http://psycle.pastnotecut.org/index.php)
|- TECH BOARD (http://psycle.pastnotecut.org/board.php?boardid=2)
|-- LuaScript questions (http://psycle.pastnotecut.org/threadid.php?boardid=2&threadid=3039)


From Angelus on 08.12.2014, 13:33:

  LuaScript questions

Hello out there.

Lua is a great option to create custom plugins based on existing ones. I started creating a Lua plugin for Mid/Side processing using several native plugins (CLICK HERE TO SEE A PREVIEW SCREENSHOT)...

... but my mind went blank manipulating channel buffers. I can eq the original input signal fine but now I need to modify internally.

I want to add left channel to right channel to output a mono signal, and then to subtract this mono signal to the left channel and to the right channel to get a side signal. Could you explain me how can I do this?

Thjs is an example function in Lua I've written:

quote:

local function Stereo2Mid(inputChannel0,inputChannel1)
midLeft = ((inputChannel0 + inputChannel1) / 2)
midRight = midLeft
return (MidLeft,MidRight)
end


... but this function leads to this error ''machine.lua:119: attempt to perform arithmetic on a userdata value''.

Thanks in advance.


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From [JAZ] on 08.12.2014, 21:34:

 

I don't have right now an in-depth knowledge of this new host.

What Stefan focused is on adding operations that could be applied to buffers, which is what you get. Basically, operating value by value in lua was quite slow.

I can only suggest to read the .pdf manual (luascriptingmanual.pdf, it's included in the docs folder).

I think the relevant chapter is 3.1, in pages 6, 7 and 8


__________________
<[JAZ]> Pa pi pa pa pa pi pa.... ;·D


From luadev on 08.12.2014, 22:01:

 

Hi Angelus,

nice that you are using lua.

>> (inputChannel0 + inputChannel1) / 2
ok, I think that is a bug and should normally work.
In general you should prefer the array object methods
to the operators(* + - /), because there is no return value
optimization and temporary arrays will be created for
evaluating the term.

Here are three versions that should work. The first is mostly
like yours, in the second example, the midLeft/midRight
arrays are just created once and not at every work call.
The last assumes that the inputChannel and your output buffer
are the same.


Code 1:

local array = require("psycle.array")

local function Stereo2Mid(inputChannel0,inputChannel1)
local size = inputChannel0:size()
-- create two new arrays
local midLeft = array.new(size)
local midRight = array.new(size)
-- mix left channel
midLeft:add(inputChannel0)
midLeft:add(inputChannel1)
midLeft:mul(0.5)
-- copy to right *1
-- Note : you can avoid this copy here if you
-- replace (*2) with self:channel(1):copy(left)
midRight:copy(midLeft)
return midLeft, midRight
end

function machine:work(num)
local left, right = Stereo2Mid(self:channel(0),self:channel(1))
self:channel(0):copy(left)
self:channel(1):copy(right) -- *2
end

Code 2:

local array = require("psycle.array")

local midLeft = array.new(256)
local midRight = array.new(256)

local function Stereo2Mid(inputChannel0,inputChannel1, out1, out2)
local size = inputChannel0:size()
-- resize out to in size
out1:resize(size)
out2:resize(size)
-- mix left channel
out1:copy(inputChannel0)
out1:add(inputChannel1)
out1:mul(0.5)
-- note this is maybe not needed if you
-- change (*2) to self:channel(1):copy(midLeft)
out2:copy(out1)
end

function machine:work(num)
Stereo2Mid(self:channel(0),self:channel(1), midLeft, midRight)
-- copies midLeft, midRight to the machine buffer
self:channel(0):copy(midLeft)
self:channel(1):copy(midRight) --*2
end


Code 3

function Stereo2Mid(inputChannel0,inputChannel1)
inputChannel0:add(inputChannel1):mul(0.5)
inputChannel0:copy(inputChannel1)
end

function machine:work(num)
self:channel(0):add(self:channel(1):mul(0.5)
self:channel(1):copy(midRight) --*2
end


__________________
42 PRINT"42":REM A FOR ALL Q 43 GOTO 42:REM FIND Q FOR A


From luadev on 08.12.2014, 22:15:

 

Sorry, Code 3 must be:

function Stereo2Mid(inputChannel0,inputChannel1)
inputChannel0:add(inputChannel1):mul(0.5)
inputChannel1:copy(inputChannel0)
end

function machine:work(num)
Stereo2Mid(self:channel(0), self:channel(1))
end


__________________
42 PRINT"42":REM A FOR ALL Q 43 GOTO 42:REM FIND Q FOR A


From Angelus on 08.12.2014, 22:51:

 

Thanks for the replies, guys!

I've already read the manual but I wasn't able to perform what I need (maybe the problem is on my side)

luadev: Thanks for the in-depth tips. I'll test them when I'll get some free time available and will inform here.


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From Angelus on 12.12.2014, 19:14:

 

Hello.

luadev: I've tried the three suggestions you shared but unfortunately without success.

Then I tried to know if there's any problem with local setbuffer on loaded Psycle machines. To that effect, I tested it on the example plugin 'plugloaddemo'. I've changed the setbuffer parameters as follows:

code:
function machine:work(num)
reverb:setbuffer({self:channel(0),self:channel(0)});
-- or even 'reverb:setbuffer({self:channel(1),self:channel(1)});'
reverb:work(num)
end


It should be sent the input buffer for left channel to both left & right channels of reverb effect (shouldn't it?)...
...but it sounds as it were set ({self:channel(0),self:channel(1)}) (send channel0 to plugin left channel, and channel1 to right channel) in both configurations.

Another issue I've found is related to the method numchannels. The sentence ''local aMSeqNumChan = numchannels()'' reports me the error message ''machine.lua:203: attempt to call global 'numchannels' (a nil value)'', within init and work methods.

PS: The lua feature rocks! Instead of tracking... I (am trying to) code.


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From [JAZ] on 12.12.2014, 20:12:

 

I should warn that there might be some vsts that will not work with lua.

There are two ways that plugins offer to receive and return the audio. One is process and the other processReplace. The former existed since VST 1 and was deprecated in 2.3, and used accumulation over the variable. The latter uses an input output schema.

Due to the way it is implemented in Psycle, when doing process, the buffer is constantly changing (swapping input and output), so it cannot be given to lua.


Said that, it probably it not the problem you talk about.


__________________
<[JAZ]> Pa pi pa pa pa pi pa.... ;·D


From Angelus on 12.12.2014, 22:14:

 

[JAZ]: I've used the example demo lua plugin to illustrate what I've observed. It happens too with my script using dw-eq.dll.


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From luadev on 12.12.2014, 22:20:

 

<< reverb:setbuffer({self:channel(0),self:channel(0)});

This means the reverb processes and modifies self:channel(0).

self:channel(1) is unmodified (reverb doesn't know this buffer) buf of course channel(1) has yet the sound data from the input machine and will route it without modification to the right output wire.

<< aMSeqNumChan = numchannels()

you need to call it with self:numchannels(), self is the instance of your machine. (In c++ you would call it this pointer)




__________________
42 PRINT"42":REM A FOR ALL Q 43 GOTO 42:REM FIND Q FOR A


From Angelus on 13.12.2014, 18:34:

 

Oh! I've forgotten that channel(1) was already there (noooooob!).

I've kept calm and I've read again your suggestions so I've been able to create the mid signal (phew!)... and the side signal!

code:
Thanks:mul(1000);

I've tried using: ''localSide0:sub(localMid0)'' (there's a misprint in the Manual subs(num) > sub(num) ) but Psycle reports me an error on lua script. Does this method works as add() does?

Next question: How do I call back the output signal from a local psycle plugin to go on doing more local functions?

PS: Thanks in advanceȘ


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From luadev on 13.12.2014, 19:08:

 

Nice that your mid signal is now working

<< ''localSide0:sub(localMid0)''

Unfortunatley, i forgot to implement sub. (I will add it to the next release.)
Please use instead for now : localSide0:add(-1*localMid0)

Second question:
You get the output of an inner plugin (like reverb in plugindemo),
if you to do two steps:
reverb:setbuffer({buf1, buf2}) -- give reverb an input buffer
reverb:work(buf1:size()) -- let reverb process the data

the output is buf1 and buf2, because
work writes the result over buf1, and buf2.
You can now continue modifying these buffers.
e.g : buf1:mul(0.5) -- reduce volume of result


__________________
42 PRINT"42":REM A FOR ALL Q 43 GOTO 42:REM FIND Q FOR A


From Angelus on 13.12.2014, 19:56:

 


luadev: It works! Thanks for your help. I hope to release my script soon.

I have in mind other plugins... time will tell.


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From Angelus on 14.12.2014, 13:25:

 

Ok... so here they are my plugins: [CLICK HERE TO DOWNLOAD THE DEMOS]

I've coded 2 plugins: aMS and aMSeq. The first one is a simple Mid/Side encoder, and the second one is a specialized plugin to perform Eq separately on both mid & side components.

There's a psy song inside to test both plugins. Read the Properties on the psy song, and the plugin help.

Suggestions, comments, IMPROVEMENTS and critics are really welcome.

Thanks in advance...

... more to come.


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From Angelus on 14.12.2014, 16:40:

 


Is Lua script plugin preset saving not implemented yet? If yes Bank manager doesn't show it. Anyway I can't find its user preset manually.


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From luadev on 14.12.2014, 21:04:

 

Congratulations for your first plugins!

<< Is Lua script plugin preset saving not implemented yet?
It's implemented. If you open View->Bank Manager, you can write a preset name and press save. If you select it again and press use, you can load your preset.

<< If yes Bank manager doesn't show it.
try to save a preset for luaris or a native plugin, to see if that works.

<< Anyway I can't find its user preset manually.
You could check first the user preset directory in the Configuration settings and check, if you have write access from your os for that directory. (My test with your plugin works. It creates a file aMS.prs in the Preset Directory)


__________________
42 PRINT"42":REM A FOR ALL Q 43 GOTO 42:REM FIND Q FOR A


From Angelus on 20.12.2014, 16:04:

 

Now I'm having problems with pattern tweaking. I can program tweaks without problems for the local Psycle native plugins in the script, but I've got errors when I tweak internal functions: They work fine if the script GUI is not shown, but Psycle shows an error that leads to a machine mute if the GUI is shown. I tried to add listeners but I'm still having problems.

I.e. I have this parameter that I want to tweak:

code:
aMSParams.MidOutput = param:newknob(''Mid Output'','''',0,1,1,1)
function aMSParams.MidOutput:display() return paramOffOn[self:val()+1] end



I have this function inside work() that carries the effect out:
code:
if aMSParams.MidOutput:val() == 0 then
aMSParams.SideOutput:setval(1);
midCh0:fillzero();
midCh1:fillzero();
end
if aMSParams.SideOutput:val() == 0 then
aMSParams.MidOutput:setval(1);
sideCh0:fillzero();
sideCh1:fillzero();
end


If the GUI is not open the action is performed correctly on tweak. But with the GUI open I've got an error window like this:

code:
---------------------------
Error
---------------------------
Machine: aMS
DLL: D:\PLUGINS\LuaScripts\Psycle 1.12.00\aMS.lua
An exception occured in module: (unknown module), function: class psycle::host::LuaProxy :: class std::basic_string std::char_traits,class std::allocator > __thiscall psycle::host::LuaProxy::get_parameter_display(int), file:
.\src\psycle\host\LuaHost.cpp:670
exception type: class std::runtime_error
function parameter:display must return a string
The machine has been set to bypassed/muted to prevent it from making the host crash.
You should save your work to a new file, and restart the host.
---------------------------
Ok
---------------------------



__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From luadev on 20.12.2014, 23:01:

 

Lua scripts parameters are internally normalized to the range of 0..1 That means, inside the patternview you tweak with 0..FFFF. FFFF means param:val() gets one. Your table local paramOffOn = {"Off", "On"} accepts as index one and two. Inside the display method you use paramOffOn[self:val()+1]. If you tweak with e.g. 7FFF, your display method gets as param value 0.5.
This means you call paramOffOn[1.5]. The paramOnOff table doesn't round and thus doesn't find the index for your On or Off String.
Solution:
Add a listener and to all your other methods with an on/off switch
:
aMSParams.MidOutput = param:newknob("Mid Output","",0,1,1,1):addlistener(self)

Now add this method to your machine, which will reacts on tweaks:

code:

function machine:ontweaked(param)
if param:id() == "MidOutput" or
param:id() == "SideOutput" or
param:id() == "MidNegative" or
param:id() == "SideNegative" then
param:setval(math.floor(param:val() + 0.5))
end
end



or if you want, that a twk cmd should work inside the range of 0 and 1 and not to 0..FFFF add instead:

code:

function machine:ontweaked(param)
if (param:id() == "MidOutput" or
param:id() == "SideOutput" or
param:id() == "MidNegative" or
param:id() == "SideNegative") and
(param:val() > 0) then
param:setval(1)
end
end





Maybe we change in the next release, that the twk cmd rounds repective to the step value.


__________________
42 PRINT"42":REM A FOR ALL Q 43 GOTO 42:REM FIND Q FOR A


From Angelus on 21.12.2014, 10:48:

 

luadev: As always... thank you so much for your tips. They worked as they should.

I hadn't seen the point of the range of values (0..1 in contrast to 0000...FFFF). That's why my listener didn't work.

Time for a new release...


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From Angelus on 28.12.2014, 15:41:

 


I'm delving more and more into lua coding. But I'm not a Jedi yet . Now I want to perform the following operation into work():

output0 = input0:add(output0prev);

...where:
- output0, input0 and output0prev are arrays;
- output0prev is an array of the previous output buffer (after some calculations);
- the arrays were defined into init() to avoid deleting the output array each time in work().

The problem now is that output0prev array size doesn't match with current output0 one, so the operation can't be carried out (sometimes is 128, sometimes is 256).

Any suggestions?

One last question: What's more efficient in Lua: to create the array into init() and then resize it into work() or to create the array directly into work() each time with array.new()?

Thanks in advance.


__________________

(???)Oo. X( :( :| :) :)) :D .cC(Psycle!)


From luadev on 29.12.2014, 06:50:

 

<< (sometimes is 128, sometimes is 256

You need to distinguish between three cases,

1. currnum == numprev
work as usual
2. currnum < numprev
work as usual, but create outputprev new
newoutputprev = oldoutputprev [curnumm .. numprev]
3. currnum > numprev
split work in parts

Cause you need to split your work, I've created in the following example a partwork method. Do your buffer process there as though it were your normal work method

Inside the regular machine work method call it with:

self:prevwork(num, self.input0, self.output0, self.output0prev)

Here's the example code with same remarks:

code:

function machine:init()
self.input0 = array.new(256)
self.output0 = array.new(256)
self.output0prev = array.new(256)
end


function machine:partwork(num, input, output, outputprev)
-- do your buffer process here as though it were your normal work method
-- test unit : convergent series with value 5
output:copy(input)
output:add(outputprev)
output:mul(0.5)
outputprev:copy(output)
end

function machine:carry(num, input, output, outputprev)
-- work num samples and create carry
-- [{start, end}] creates a new sub array with a copy from start to end
local carry = outputprev[{num, outputprev:size()}]
outputprev:resize(num)
self:partwork(num, input, output, outputprev)
outputprev:resize(carry:size())
outputprev:copy(carry)
end

function machine:split(num, input, output, outputprev)
-- avoid modifying the input array
local tmp = array.new(num)
tmp:copy(input)
input = tmp
-- split work
local pos = 0
while (num > outputprev:size()) do
local prevnum = outputprev:size()
-- [{start, end}] creates a new sub array with a copy from start to end
local inputpost = input[{prevnum, num}]
local outputprevold = outputprev[{0, prevnum}]
input:resize(prevnum)
local out = array.new(prevnum)
self:partwork(prevnum, input, out, outputprev)
-- copy part work to output
output:copy(pos, out) -- pos: target start index of copy
pos = pos + prevnum
input = inputpost
-- concatenates output0prevold..output0prev
local outputprevnew = outputprevold..outputprev
-- copy back to outputprev
outputprev:resize(outputprevnew:size())
outputprev:copy(outputprevnew)
num = num - prevnum
end
if (num < outputprev:size()) then
local out = array.new(num)
self:carry(num, input, out, outputprev)
-- copy rest of part work to output
output:copy(pos, out)
end
end

function machine:prevwork(num, input, output, outputprev)
-- check size
local prevnum = outputprev:size()
if num == prevnum then
-- it's equal, just work all
self:partwork(num, input, output, outputprev)
elseif num < prevnum then
-- num is less, work num samples and create carry array for next
-- work turn
self:carry(num, input, output, outputprev)
elseif num > prevnum then
-- num is greater, split work in parts
self:split(num, input, output, outputprev)
end
end

function machine:work(num)
-- test unit
self.output0:resize(num)
self.input0:resize(num)
self.input0:fill(5) -- or copy self:channel(0)
-- start your work
self:prevwork(num, self.input0, self.output0, self.output0prev)
-- this works, too, if you want that input writes over the output buffer:
-- self:channel(0): input
-- self:prevwork(num, self:channel(0), self:channel(0), self.output0prev)
-- self:channel(0): gets as result the output
-- or if you use another out buffer copy out to the machine buffer
-- self:channel(0):copy(self.output0)
-- output terminal for debugging
-- psycle.output(self.output0:tostring())
end




Question2:

Yes, in general it's faster, because mostly you get 256 num samples from psycle and if the array length hasn't changed, resize will just do nothing. To avoid global variables, you could create your arrays as object variables inside the init method ( function machine:init() self.output0 = array.new(256) end. Inside your machine methods you acces output0 with self.output0. function machine:work(num) self:output0:resize(num) end


__________________
42 PRINT"42":REM A FOR ALL Q 43 GOTO 42:REM FIND Q FOR A

Powered by: Burning Board 1.0 Beta 4.5eEnglish Translation by AnnaFan
Copyright © 2001 by WoltLab