diff --git a/lua2SC/lua/ide/ide_lane.lua b/lua2SC/lua/ide/ide_lane.lua index f7931ee..7d1db85 100644 --- a/lua2SC/lua/ide/ide_lane.lua +++ b/lua2SC/lua/ide/ide_lane.lua @@ -52,7 +52,11 @@ function ide_main() end function ide_lane() local function MidiOpen(opt) return mainlinda:send("MidiOpen",opt) end - local function MidiClose() return mainlinda:send("MidiClose",1) end + local function MidiClose() + local tmplinda = lanes.linda() + mainlinda:send("MidiClose",tmplinda) + tmplinda:receive("MidiClose_done") + end local process_gen=lanes.gen("*",--"base,math,os,package,string,table", { cancelstep=10000, diff --git a/lua2SC/lua/ide/ide_server.lua b/lua2SC/lua/ide/ide_server.lua index 7b26707..f76c111 100644 --- a/lua2SC/lua/ide/ide_server.lua +++ b/lua2SC/lua/ide/ide_server.lua @@ -1,6 +1,7 @@ --to comunicate via linda with server local M = {} - +M.linda = {} --for using without init +function M.linda:send() end function M:new(o) o = o or {} setmetatable(o, self) diff --git a/lua2SC/lua/ide/oscfunc.lua b/lua2SC/lua/ide/oscfunc.lua deleted file mode 100644 index f63fb22..0000000 --- a/lua2SC/lua/ide/oscfunc.lua +++ /dev/null @@ -1,44 +0,0 @@ ------------------------------------------------------------ -OSCFuncLinda = idlelinda -OSCFunc={filters={}} -function OSCFunc.newfilter(path,template,func,runonce,block,alt_linda) - local handleOSCFuncLinda = alt_linda or OSCFuncLinda - OSCFunc.filters[path] = OSCFunc.filters[path] or {} - OSCFunc.filters[path][#OSCFunc.filters[path]+1] ={template=template,func=func,runonce=runonce} - if block then -- TODO: it is too slow, may be having a dedicated linda for that ... - local tmplinda = lanes.linda() - udpsclinda:send("addFilter",{path,handleOSCFuncLinda,tmplinda}) - tmplinda:receive("addFilterResponse") - else - udpsclinda:send("addFilter",{path,handleOSCFuncLinda}) - end -end -function OSCFunc.clearfilters(path,template,alt_linda) - local handleOSCFuncLinda = alt_linda or OSCFuncLinda - --print("OSCFunc.clearfilters ",path," ",template) - udpsclinda:send("clearFilter",{path,handleOSCFuncLinda}) - if OSCFunc.filters[path] then - for i,filter in pairs(OSCFunc.filters[path]) do - if (template==nil) or (template==filter.template) then - OSCFunc.filters[path][i]=nil - print(" is done OSCFunc.clearfilters ",path," ",template) - end - end - end -end -function OSCFunc.handleOSCReceive(msg) - if msg[1]=="/fail" then - print(tb2st(msg)) - end - if OSCFunc.filters[msg[1]] then - for i,filter in pairs(OSCFunc.filters[msg[1]]) do - if (filter.template=="ALL") or (msg[2][1]==filter.template) then - filter.func(msg) - if filter.runonce then - OSCFunc.filters[msg[1]][i]=nil - end - end - end - end -end ------------------------------------------- diff --git a/lua2SC/lua/ide/scriptgui.lua b/lua2SC/lua/ide/scriptgui.lua index 97dcaf1..bab8684 100644 --- a/lua2SC/lua/ide/scriptgui.lua +++ b/lua2SC/lua/ide/scriptgui.lua @@ -448,10 +448,14 @@ function wxFuncGraph3(parent,name,label,id,co) local str=string.format("%.2f",lab) dc:DrawText(str,0,y1) end - dc:SetPen(wx.wxGREEN_PEN) - dc:SetBrush(wx.wxGREEN_BRUSH) + local pens = {wx.wxCYAN_PEN,wx.wxGREEN_PEN,wx.wxYELLOW_PEN,wx.wxRED_PEN} --wx.wxBLUE_PEN + local brushes = {wx.wxCYAN_BRUSH,wx.wxGREEN_BRUSH,wx.wxYELLOW_BRUSH,wx.wxRED_BRUSH} --wx.wxBLUE_BRUSH, local maxbin=#GraphClass.values[1] - for _,vals in ipairs(GraphClass.values) do + for npen,vals in ipairs(GraphClass.values) do + npen = npen%#pens + npen = npen==0 and #pens or npen + dc:SetPen(pens[npen]) + dc:SetBrush(brushes[npen]) if maxbin > 1 then --local vals = GraphClass.value local x @@ -467,9 +471,10 @@ function wxFuncGraph3(parent,name,label,id,co) --prtable("points",points) dc:DrawLines(points,0,height) end - end dc:SetPen(wx.wxNullPen) dc:SetBrush(wx.wxNullBrush) + end + end diff --git a/lua2SC/lua/num/filter_design.lua b/lua2SC/lua/num/filter_design.lua index 24861ca..b2d9086 100644 --- a/lua2SC/lua/num/filter_design.lua +++ b/lua2SC/lua/num/filter_design.lua @@ -1,5 +1,6 @@ --inherits from polynomial.lua for filter design transfer functions --by Victor Bombi +require"sc.utils" require"num.polynomial" filterpoly_mt = deepcopy(RationalPoly_mt) --setmetatable(filterpoly_mt,filterpoly_mt) @@ -70,6 +71,7 @@ function filterpoly_mt.GroupDelay(H,om) return H.grupdelf(om) end function FilterPoly(B,A) + if B.isRatPoly and A==nil then return setmetatable(B,filterpoly_mt) end local res = ZRatPoly(B,A) return setmetatable(res, filterpoly_mt) end @@ -371,7 +373,7 @@ function Durbin_recursion(Hz,fix) --assert(math.abs(Kaes[i]) < 1,"inestable filter coeffs in Durbin K:"..i..tostring(Kaes[i])) if math.abs(Kaes[i]) >=1 then prtable(Kaes) - error() + print"error coef" end HzN = (HzN - Kaes[i]*Flip(HzN))/(1 - Kaes[i]*Kaes[i]) HzN = HzN:Simplify():Normalize() diff --git a/lua2SC/lua/num/matrix.lua b/lua2SC/lua/num/matrix.lua new file mode 100644 index 0000000..17dba4d --- /dev/null +++ b/lua2SC/lua/num/matrix.lua @@ -0,0 +1,118 @@ +local matrix_mt = {} +matrix_mt.__index = matrix_mt +function matrix(t) + return setmetatable(t,matrix_mt) +end +local function is_matrix(a) + return getmetatable(a)==matrix_mt +end +function matrix_mt:size() + return #self,#self[1] +end +function matrix_mt:new(m,n) -- Define new Matrix + local mt = {} -- Make new array for Matrix + for i=1,m do mt[i] = {} end -- Null matrix elements + return matrix(mt) -- Set Matrix metatable +end +function matrix_mt.__mul(a,b) + if not is_matrix(a) then a,b = b,a end + local m,n = a:size() + local res = a:new(m,n) + if not is_matrix(b) then + for i=1,m do for j=1,n do res[i][j] = b*a[i][j] end end + else + local mb,nb = b:size() + assert(n==mb) + for i=1,m do + for j=1,nb do + local sum = 0 + for k=1,n do sum = sum + a[i][k]*b[k][j] end + res[i][j] = sum + end + end + end + return res +end +function matrix_mt.__tostring(mx) -- Convert to string for printing + local m,n = mx:size() + local s = '{{' + for i=1,m do + for j=1,n-1 do + s = s..(tostring(mx[i][j]) or '..')..', ' end + s = s..(tostring(mx[i][n]) or '..')..'}' + if i==m then s = s..'}' else s = s..',\n{' end + end + return s -- Printable string +end +--[[ +require"num.complex" +require"num.filter_design" + +function MM(k) + return matrix{{Zpow(1),k*Zpow(-1)},{k*Zpow(1),Zpow(-1)}}*(1/(k+1)) +end + +function KLJunctionGain(kaes,rG,rL) + kaes = TA(kaes):reverse() + local res = matrix{{1},{rL}} + for i,k in ipairs(kaes) do + res = MM(k)*res + end + res = matrix{{Zpow(1),-rG*Zpow(-1)}}*res + return 1/res[1][1] +end +function KLJunctionGain2(kaes,rG,rL) + + local res = matrix{{Zpow(1),-rG*Zpow(-1)}}--matrix{{1},{rL}} + for i,k in ipairs(kaes) do + res = res*MM(k) + end + res = res*matrix{{1},{rL}} + return 1/res[1][1] +end + +require"sc.utils" +require"sc.utilsstream" +Tract = require"num.Tract"(18,nil,nil,false) + + +function Areas2KK(AA) + local Kaes = {} + + for i=2,#AA do + local num = (AA[i-1]-AA[i]) + local den = (AA[i]+AA[i-1]) + if AA[i]==math.huge then + Kaes[i-1] = 1 + else + if num == 0 or den == 0 then + Kaes[i-1] = 0 + else + Kaes[i-1] = num/den + end + end + end + return Kaes +end + +areas = Tract.areas.E +kaes = Areas2KK(areas) + +print(KLJunctionGain({1,2,3},0.95,0.97)) +print(KLJunctionGain2({1,2,3},0.95,0.97)) + +fil = KLJunctionGain(kaes,0.95,0.97) + +print(MM(3)) +pp = FilterPoly({1,1},{1})--*Zpow(-1) +print(pp) +pp2 = RatPoly({1,1},{1})--*Zpow(-1) +print(pp2) +aa = matrix{{1,1},{0,1}} +vec = matrix{{2,3}} +vec2 = matrix{{2},{3}} +print(aa:size()) + +print(aa*vec2) +--]] + diff --git a/lua2SC/lua/num/torational.lua b/lua2SC/lua/num/torational.lua new file mode 100644 index 0000000..5ae2f5f --- /dev/null +++ b/lua2SC/lua/num/torational.lua @@ -0,0 +1,78 @@ +--[[/* +** find rational approximation to given real number +** David Eppstein / UC Irvine / 8 Aug 1993 +** +** With corrections from Arno Formella, May 2008 +** +** usage: a.out r d +** r is real number to approx +** d is the maximum denominator allowed +** +** based on the theory of continued fractions +** if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...))) +** then best approximation is found by truncating this series +** (with some adjustments in the last term). +** +** Note the fraction can be recovered as the first column of the matrix +** ( a1 1 ) ( a2 1 ) ( a3 1 ) ... +** ( 1 0 ) ( 1 0 ) ( 1 0 ) +** Instead of keeping the sequence of continued fraction terms, +** we just keep the last partial product of these matrices. +*/ +--]] +function GCD(a,b) + local r + if a < b then b,a = a,b end + while true do + r = a%b + if r == 0 then return b end + --print(a,b,r) + a,b = b,r + end +end +--always gives reduced fractions, dont need GCD +function torational(x,maxden) + local startx = x + -- initialize matrix */ + local m00, m11 = 1,1; + local m01, m10 = 0,0; + + -- loop finding terms until denom gets too big */ + local t + local ai = math.floor(x) + while (m10 * ai + m11 <= maxden) do + print(ai) + t = m00 * ai + m01; + m01 = m00; + m00 = t; + t = m10 * ai + m11; + m11 = m10; + m10 = t; + if(x == ai) then break end -- AF: division by zero + x = 1/(x - ai); + if(x >0x7FFFFFFF) then break end -- AF: representation failure + ai = math.floor(x) + end + + -- now remaining x is between 0 and 1/ai */ + -- approx as either 0 or 1/m where m is max that will fit in maxden */ + -- first try zero */ + -- print( string.format("%d/%d, error = %e\n", m[0][0], m[1][0],startx - ( m[0][0] / m[1][0]))); + return m00,m10,startx - ( m00 / m10) + + -- now try other possibility */ + --ai = (maxden - m[1][1]) / m[1][0]; + --m[0][0] = m[0][0] * ai + m[0][1]; + --m[1][0] = m[1][0] * ai + m[1][1]; + --print(string.format("%d/%d, error = %e\n", m[0][0], m[1][0], startx - ( m[0][0] / m[1][0]))); +end + +--print(torational(2*5*7/(11*3*13),10000)) +--print(torational((3/2)^12,1e3)) +--print(GCD(1e17*13,1e17*11)) +--[[ +local N,M = torational(math.pi,1000) +local gcd = MCD(N,M) +print(N,M) +print(N/gcd,M/gcd) +--]] \ No newline at end of file diff --git a/lua2SC/lua/num/tract.lua b/lua2SC/lua/num/tract.lua index 64637d3..ddafb9d 100644 --- a/lua2SC/lua/num/tract.lua +++ b/lua2SC/lua/num/tract.lua @@ -1,11 +1,129 @@ -return function(NN,DEFDUR,DEFRATE) +local function MakeTractSynth(Tract,syname,args,excifunc) + local defargs = {out=0, gate=1,t_gate=1, freq=60,amp=0.6,pan=0,lossG=0.97,lossL=0.97,lossN=0.95,lossF=1,area1len=8*22/17.5,Gain=1,lmix=1,nmix=1,Ar=Ref(TA():Fill(#Tract.areas.A,1.5)),ArN=Ref(Tract.AreaNoseC),lenT = 17.5,df=Ref(TA():Fill(#Tract.areas.A,1)),noiseloc=0,glot=1,noisef=Ref{2500,7500},noisebw=Ref{1,1},plosive=0,fA=1,fAc=1,thlev=0,fexci=6000,fout=8000} + local function deffunc(excifunc) + return function(newgt) + setfenv(excifunc,newgt) + local exci = excifunc() + exci = exci*amp*Gain + local env=EnvGen.ar(Env.asr(0.001, 1, 0.1), gate, nil,nil,nil,2); + local nsecs = math.floor(#Ar*0.5 + 0.5) + local pend = (1- fAc)/(nsecs - 1) + for ii=1,nsecs do + Ar[ii] = Ar[ii]*(fAc + (ii-1)*pend) + --df[ii] = df[ii]*(fAc + (ii-1)*pend) + end + local noise = WhiteNoise.ar()*0.1*amp --*EnvGen.ar(Env({0,0,1},{0,0.08}),t_gate) + noise = BBandPass.ar(noise,noisef,noisebw) --Resonz.ar(noise,noisef,0.08) + noise = Mix(noise) + lossF = (17/#Tract.areas.A)*4e-3*lossF + --lenT = EnvGen.kr(Env({lenT,lenT},{rate}),t_gate) + local lenf = SampleRate.ir()*lenT*fA/(35000*#Ar) + local dels = df*lenf + local signal = HumanVNdel.ar(exci,noise,noiseloc,lossF,lossG,-lossL,-lossN,lmix,nmix,area1len,dels,Ar,ArN)*env + local throat = LPF.ar(exci,400) + signal = signal + throat*thlev + signal = LPF.ar(signal,fout) + signal = LeakDC.ar(signal,0.91) + signal = Pan2.ar(signal*3,pan); + return Out.ar(out, signal) + end + end + for k,v in pairs(args) do + defargs[k]=v + end + Tract[syname] = SynthDef(syname..Tract.NN,defargs,deffunc(excifunc)):store() +end + +local function Rd2Times(Rd) + local ra = (0.048*Rd-0.01) + local rk = (0.118*Rd+0.224) + local den = 0.44*Rd-4*ra*(0.5 + 1.2*rk) + local rg = (0.5 + 1.2*rk)*rk/den + local Ta = ra --*to + local Tp = 1/(2*rg) + local Te = Tp*(rk + 1) + + local num =34.83839405219 -9.3865444973664*Rd + local den = 1 + 7.9184898828409*Rd + alpha = num/den + + --local Ee = freq/(110*Rd) + local Ee = 1/(Rd) + return Tp,Te,Ta,alpha,Ee +end + +local function MakeCoralSynth(Tract,name,freqs) + Tract:MakeSynth(name,{Rd=0.3,namp=0.04,nwidth=0.4,vibrate=5,vibdeph=0.01}, + function() + + --local freqs = TA():series(10,1 - 0.002*5,0.002) + freqs = freq * freqs + local vibratoF = Vibrato.kr{freqs, rate= vibrate, depth= vibdeph, delay= 0.0, onset= 0, rateVariation= 0.5, depthVariation= 0.1, iphase = 0} + + local Tp,Te,Ta,alpha,Ee = Rd2Times(Rd) + + local exci = LFglottal.ar(vibratoF,Tp,Te,Ta,alpha,namp,nwidth)*glot*3*Ee + + exci = WhiteNoise.ar()*plosive + exci + --exci = exci:Doi(function(v,i) return DelayC.ar(v,0.1,Rand(0,0.1)) end) + --exci = exci:Doi(function(v,i) return DelayC.ar(v,0.2,LFDNoise3.kr(0.1,0.05,0.05))*SinOsc.ar(5,Rand(-math.pi,math.pi),0.3,0.7) end) + exci = exci:Doi(function(v,i) return DelayC.ar(v,0.2,LFDNoise3.kr(0.1,0.05,0.05))*LFDNoise3.kr(5,0.3,0.7) end) + exci = Mix(exci) + exci = LPF.ar(exci,fexci) + --exci = BrownNoise.ar()*plosive*EnvGen.ar(Env({0,0,1},{0.02,0.04}),t_gate) + exci + return exci + end) +end + + +local function InitSynths(Tract,doinit) + function Tract:MakeSynth(syname,args,excifunc) + return MakeTractSynth(self,syname,args,excifunc) + end + function Tract:MakeCoralSynth(name,freqs) + MakeCoralSynth(self,name,freqs) + end + + if doinit then + local fratio = midi2ratio(0.05) + local freqs = TA():gseries(10,1 * fratio^(-5),fratio) + Tract:MakeCoralSynth("coral",freqs) + + + Tract:MakeSynth("sinteRd",{Rd=0.3,alpha=3.2,namp=0.04,nwidth=0.4,vibrate=5,vibdeph=0.01},function() + local vibratoF = Vibrato.kr{freq, rate= vibrate, depth= vibdeph, delay= 0.0, onset= 0, rateVariation= 0.5, depthVariation= 0.1, iphase = 0} + local Tp,Te,Ta,alpha,Ee = Rd2Times(Rd) + + local exci = LFglottal.ar(vibratoF,Tp,Te,Ta,alpha,namp,nwidth)*glot*3*Ee + --local exci = VeldhuisGlot.ar(vibratoF,Tp,Te,Ta,namp,nwidth)*glot*3*Ee + exci = WhiteNoise.ar()*plosive + exci + exci = Mix(exci) + exci = LPF.ar(exci,fexci) + --exci = BrownNoise.ar()*plosive*EnvGen.ar(Env({0,0,1},{0.02,0.04}),t_gate) + exci + return exci + end) + + Tract:MakeSynth("sinte_chen",{OQ=0.8,asym=0.6,Sop=0.4,Scp=0.12,vibrate=5},function() + local vibratoF = Vibrato.kr{freq, rate= vibrate, depth= 0.01, delay= 0.0, onset= 0, rateVariation= 0.5, depthVariation= 0.1, iphase = 0} + local exci = ChenglottalU.ar(vibratoF,OQ,asym,Sop,Scp)*glot*3 + exci = HPZ1.ar(exci) + exci = LPF.ar(exci,fexci)*30 + exci = WhiteNoise.ar()*plosive + exci + return exci + end) + end +end + +local function Init(NN,DEFDUR,DEFRATE,doinit) + if (doinit==nil) then doinit=true end DEFDUR = DEFDUR or 0.1 DEFRATE = DEFRATE or 0.1 local MINIMAL = 0 --1e-9 local required = string.format("num.tract_male%d",NN) local Tract = require(required) +Tract.NN = NN local function copy_phoneme(a,b) for k,v in pairs(Tract) do if type(v)=="table" and v[b] then @@ -40,7 +158,7 @@ Tract.glot.K = 0 Tract.glot.G = 1 Tract.glot.H = 0 Tract.plosive = {} -Tract.plosive.K = 1 --db2amp(-15) +Tract.plosive.K = 1 --0.5 --db2amp(-15) Tract.plosive.G = 0 Tract.plosive.P = 0.5 Tract.plosive.B = 0.5 @@ -49,7 +167,7 @@ Tract.plosive.D = 0 Tract.plosive.H = 0.35 -Tract["plosive"]["R"] = 0 +Tract.plosive.R = 0 --------------gains Tract["gains"] = {} Tract.gains.Ate = db2amp(6) @@ -58,15 +176,15 @@ Tract.gains.Ite = db2amp(5) Tract.gains.Ote = db2amp(0) Tract.gains.Ute = db2amp(0) -Tract.gains.B = db2amp(-61) +Tract.gains.B = db2amp(-45)-- -61) Tract.gains.P = db2amp(-25) Tract.gains.D = db2amp(-15) Tract.gains.T = db2amp(-20) -Tract.gains.R = db2amp(-5) --db2amp(-10) -Tract.gains.K = 1 --db2amp(-14) +Tract.gains.R = db2amp(-25) --db2amp(-5) +Tract.gains.K = db2amp(-5) Tract.gains.G = db2amp(-20) Tract["gains"]["O"] = db2amp(-4) -Tract.gains.L = db2amp(0) +Tract.gains.L = db2amp(-5) Tract["gains"]["E"] = 1.4125375446228 Tract["gains"]["I"] = db2amp(-3) Tract["gains"]["M"] = db2amp(-12) @@ -90,7 +208,7 @@ Tract.rate.P = 0 --0.05 --0.02 --0.01 Tract.rate.H = 0.01 --Tract.rate[" "] = 0.1 -Tract.rate.L = 0 +Tract.rate.L = 0.1 --0.02 Tract.dur = {} Tract.dur.Z = 0.1 Tract.dur.R = 0.05 @@ -118,7 +236,14 @@ copy_phoneme("Mv","M") Tract.vocals = {A=true,Ae=true,E=true,I=true,I2=true,O=true,U1=true,U=true,Mv=true,Nv=true,[" "]=false,_v=true,Ate=true,Ete=true,Ite=true,Ote=true,Ute=true} ------------------------------------------------------------------------------------------- copy_phoneme("_"," ") -Tract.dur._ = 0.005 +Tract.dur._ = 0.00 +Tract.krate._ = 0.00 +copy_phoneme("Eq","E") +Tract.rate.Eq = 0.0 +copy_phoneme("_q"," ") +Tract.rate._q = 0.0 +Tract.dur._q = 0.0 +Tract.krate._q = 0.0 --[[ --------convert zeros to tiny for allowing cub and exp curves if true then @@ -149,335 +274,8 @@ end end --]] ----------------------------------------------------------------------------------- -function GlottalRossB(N,fac1,fac2) - local datos = {} - N = N or 100 - fac1 = fac1 or 0.4 - fac2= fac2 or 0.36 - fac2 = fac1 + fac2 - local N1 = math.floor(fac1*N) - local N2 = math.floor(fac2*N) - for i=0,N do - - if i 0 or midi_out_opened then lanes.timer( midilinda, "miditimer", 0.01, 0) --0.01 ) while(true) do - local key,val= midilinda:receive( "miditimer","midiWriteShort","exit_midi_thread" ) + local key,val= midilinda:receive("exit_midi_thread", "miditimer","midiWriteShort" ) --if key=="miditimer" and #midiin > 0 then if key=="miditimer" then @@ -154,8 +147,18 @@ function M.midi_thread(inp,out) --prtable(val) end elseif key=="exit_midi_thread" then - midilinda:send("exit_midi_thread_done",1) - --print(key,val) + io.write"PMIDI: exit_midi_thread arrived" + print("PMIDI: midi_thread closing devices") + for i,v in ipairs( midiout) do + print("closing ",v.name) + v.stream:close() + end + for i,v in ipairs( midiin) do + print("closing ",v.name) + v.stream:close() + end + print("PMIDI: midi_thread closed devices") + callbacklinda:send("exit_midi_thread_done",1) break end end diff --git a/lua2SC/lua/sc/MetronomLanes.lua b/lua2SC/lua/sc/MetronomLanes.lua index 7010093..a53cb53 100644 --- a/lua2SC/lua/sc/MetronomLanes.lua +++ b/lua2SC/lua/sc/MetronomLanes.lua @@ -27,6 +27,7 @@ function getHostTime() end function theMetro:GOTO(beat) self:play(nil,beat) + self.oldppqPos = beat - self.frame --for calling inside frame_callback end function theMetro:play(bpm,beat,run,rate) if rate then self.rate=rate end diff --git a/lua2SC/lua/sc/PhISEMSynth.lua b/lua2SC/lua/sc/PhISEMSynth.lua index b94cb34..246a933 100644 --- a/lua2SC/lua/sc/PhISEMSynth.lua +++ b/lua2SC/lua/sc/PhISEMSynth.lua @@ -152,7 +152,7 @@ function PhISEM:MakeSynth(name,preset) local t = makeShynthFromPreset(self.presets[preset]) -------------------------------------- ---[[ - SynthDef(name, {out = 0,gate=1,freq = 400,pan = 0,amp=0.1, + SynthDef(name, {out = 0,gate=1,ffac = 1,pan = 0,amp=0.1, freqs=Ref(t.freqs),sys_dec=t.sys_dec,sound_dec=t.sound_dec,amps=Ref(t.amps),decays=Ref(t.decays),f_rand=Ref(t.freq_rand),objects=t.objects,GAIN=t.GAIN,Zeros=Ref(t.Zeros)}, function() local objectsm = objects:max(2) --local pulses = Dust.ar(objects*43, 1) @@ -166,7 +166,7 @@ freqs=Ref(t.freqs),sys_dec=t.sys_dec,sound_dec=t.sound_dec,amps=Ref(t.amps),deca --local driver = WhiteNoise.ar(1)*Integrator.ar(sysdriver,sound_dec) local driver = WhiteNoise.ar(1)*Decay.ar(sysdriver,sound_dec) --local freqs2 = freqs * (1.0 + Gate.ar((freq_rand * TA{WhiteNoise.ar()}),pulses)) - local freqs2 = freqs * (1 + TRand.ar( -f_rand, f_rand, pulses)) + local freqs2 = ffac*freqs * (1 + TRand.ar( -f_rand, f_rand, pulses)) local signal = Mix(Ringz.ar(driver,freqs2,decays,amps)) --local e2 = EnvGen.kr(Env.asr(attack, 0.5, 0.1),gate,nil,nil,nil,2); --signal = sysdriver diff --git a/lua2SC/lua/sc/ctrl_bus.lua b/lua2SC/lua/sc/ctrl_bus.lua index 6a670f6..7d34837 100644 --- a/lua2SC/lua/sc/ctrl_bus.lua +++ b/lua2SC/lua/sc/ctrl_bus.lua @@ -40,7 +40,8 @@ function SendCtrlSynth(synname,lista,paramname,player,beatTime) local on = {"/n_set", {player.ctrl_buses.nodes[paramname],"t_gate",1}} getMsgLista(on,lista) --sendBundle(on,theMetro:ppq2time(beatTime)) - table.insert(bundle,on) + --table.insert(bundle,on) + bundle[#bundle+1] = on --local mapmsg = {"/n_map",{player.node,paramname,{"int32",player.ctrl_buses.buses[paramname]}}} --sendBundle(mapmsg,theMetro:ppq2time(beatTime)) else @@ -51,13 +52,15 @@ function SendCtrlSynth(synname,lista,paramname,player,beatTime) local on = {"/s_new", {synname, node, 0, player.ctrl_group, "bus", {"int32",bus},"t_gate",1}} getMsgLista(on,lista) --sendBundle(on,theMetro:ppq2time(beatTime)) - table.insert(bundle,on) + --table.insert(bundle,on) + bundle[#bundle+1] = on --local mapmsg = {"/n_map",{player.node,paramname,{"int32",bus}}} --sendBundle(mapmsg,theMetro:ppq2time(beatTime)) --prerror("new envel one ",synname,paramname) end local mapmsg = {"/n_map",{player.node,paramname,{"int32",player.ctrl_buses.buses[paramname]}}} - table.insert(bundle,mapmsg) + --table.insert(bundle,mapmsg) + bundle[#bundle+1] = mapmsg --sendMultiBundle(theMetro:ppq2time(beatTime),bundle) return bundle end @@ -94,7 +97,8 @@ function SendCtrlSynth_ar(synname,envel_ar,paramname,player,beatTime) local nodes = player.ctrl_buses.nodes[paramname] for i=1,#envel_ar do local node = GetNode() - table.insert(nodes,node) + --table.insert(nodes,node) + nodes[#nodes+1]=node local bus = firstbus + i -1 local on = {"/s_new", {synname, node, 0, player.ctrl_group, "bus", {"int32",bus},"t_gate",1}} getMsgLista(on,envel_ar[i]) @@ -107,7 +111,7 @@ function SendCtrlSynth_ar(synname,envel_ar,paramname,player,beatTime) end local mapmsg = {"/n_mapn",{player.node,paramname,{"int32",firstbus},{"int32",#envel_ar}}} --sendBundle(mapmsg,theMetro:ppq2time(beatTime)) - table.insert(bundle,mapmsg) + bundle[#bundle+1] = mapmsg --sendMultiBundle(theMetro:ppq2time(beatTime),bundle) return bundle end @@ -176,6 +180,9 @@ ctrl_mapper.__mul = function (a,b) end return C end +ctrl_mapper.__div = function (a,b) + return a*(1/b) +end -------------------------- function RAMP(inip,endp,time) @@ -218,6 +225,10 @@ SynthDef("ENVELm",{lev=Ref(TA():Fill(MAX_ENVEL_STEPS +1,0)),tim=Ref(TA():Fill(MA function() ReplaceOut.kr(bus,EnvGen.kr{Env(lev,tim,cur),t_gate})--doneAction=2}) end):store() +SynthDef("ENVELm_st",{lev=Ref(TA():Fill(MAX_ENVEL_STEPS +1,0)),tim=Ref(TA():Fill(MAX_ENVEL_STEPS,0)),cur=Ref(TA():Fill(MAX_ENVEL_STEPS,0)),bus=0,t_gate=1}, + function() + ReplaceOut.kr(bus,EnvGen.kr{Env.new_str_curves(lev,tim,cur),t_gate})--doneAction=2}) + end):store() --]] end) @@ -393,13 +404,20 @@ function ENVm(levels,times,curves,relative,istime) levels = LastPad(levels,MAX_ENVEL_STEPS + 1) times = ZeroPad(times,MAX_ENVEL_STEPS) local cur = (type(curves)=="table") and curves or {curves} - cur = ZeroPad(cur,MAX_ENVEL_STEPS) - return levels,times,cur --Env(levels,times,curves):prAsArray() + local synthname = "ENVELm" + for i,v in ipairs(cur) do + if type(v)=="string" then + synthname = "ENVELm_st" + cur[i] = Env.shapeNames[v] + end + end + cur = LastPad(cur,MAX_ENVEL_STEPS) + return levels,times,cur,synthname --Env(levels,times,curves):prAsArray() end curves = curves or 0 local ctmap = ctrl_mapper:new{levels=levels,times=times} function ctmap:verb(paramname,player,beatTime,beatLen) - local lev,tim,cur + local lev,tim,cur,synthname if type(levels)=="function" then lev = levels(player) else @@ -415,14 +433,13 @@ function ENVm(levels,times,curves,relative,istime) else cur = curves end - lev,tim,cur = E2ppqm(lev,tim,cur,relative and beatLen or 1,istime) + lev,tim,cur,synthname = E2ppqm(lev,tim,cur,relative and beatLen or 1,istime) local lev_ar = Envflop(lev,tim,cur) if #lev_ar == 1 then - return SendCtrlSynth("ENVELm",lev_ar[1],paramname,player,beatTime) + return SendCtrlSynth(synthname,lev_ar[1],paramname,player,beatTime) else - return SendCtrlSynth_ar("ENVELm",lev_ar,paramname,player,beatTime) + return SendCtrlSynth_ar(synthname,lev_ar,paramname,player,beatTime) end - --return 0 end return ctmap end diff --git a/lua2SC/lua/sc/debugger.lua b/lua2SC/lua/sc/debugger.lua index 5e40fc7..1fd6211 100644 --- a/lua2SC/lua/sc/debugger.lua +++ b/lua2SC/lua/sc/debugger.lua @@ -170,9 +170,9 @@ function Debugger:init(bp,maxdeph) self.laststacklevel = {} self.functable = {} self.breakpoints = bp.breakpoints or {} - repeat - local key,val= debuggerlinda:receive(0,"continue","debug_exit","step_into","step_over","step_out","brpoints","break") - until val==nil + --clean linda + local keys = {"continue","debug_exit","step_into","step_over","step_out","brpoints","break"} + for i,v in ipairs(keys) do debuggerlinda:set(v) end --for debugging coroutines local oldcocreate = coroutine.create coroutine.create = function(f) diff --git a/lua2SC/lua/sc/init.lua b/lua2SC/lua/sc/init.lua index 195c37f..79ea005 100644 --- a/lua2SC/lua/sc/init.lua +++ b/lua2SC/lua/sc/init.lua @@ -24,4 +24,5 @@ else error("typeshed is nil") end --MASTER_INIT1() +--require"sc.lilypond" table.insert(initCbCallbacks,1,MASTER_INIT1) diff --git a/lua2SC/lua/sc/lilypond.lua b/lua2SC/lua/sc/lilypond.lua new file mode 100644 index 0000000..712ef4a --- /dev/null +++ b/lua2SC/lua/sc/lilypond.lua @@ -0,0 +1,344 @@ +local LILY = {} +function LILY:Gen() end +if typerun==1 then return LILY end + +require"sc.oscfunc"(scriptlinda) +require"sc.callback_wrappers" +require"sc.sc_comm" +--InitSCCOMM() +require"sc.gui" +if typeshed == false then + require"sc.playerssc" +elseif typeshed then + require"sc.playersscSCH" +else + error("typeshed is nil") +end +--table.insert(initCbCallbacks,MASTER_INIT1) +require"sc.miditoosc" +require"sc.playersscgui" +require"sc.scbuffer" +require"sc.ctrl_bus" +require"sc.named_events" +if typeshed == false then + require"sc.MetronomLanes" +elseif typeshed then + require"sc.MetronomLanesSCH" +else + error("typeshed is nil") +end + + sendBundle = function(msg,ti) + end + sendMultiBundle = function(ti,msg) + end + sendBlocked = function(msg) + return {"/done",{}} + end +--MASTER_INIT1() +table.insert(initCbCallbacks,1,MASTER_INIT1) +------------------------------------- + + +local notenumbers = {[0]="c","cis","d","dis","e","f","fis","g","gis","a","ais","b"} +local notenumbers = {[0]="c","des","d","ees","e","f","ges","g","aes","a","bes","b"} +local function numberToNote(number,numbernote) + numbernote = numbernote or notenumbers + if number == REST or number==NOP then return "r",0 end + local numberround = math.floor(number + 0.5) + local octave = math.floor(numberround / 12) - 1 + local note = numberround % 12 + octave = octave - 3 --?? + local octavestr + if octave >=0 then + octavestr = ("'"):rep(octave) + else + octavestr = (","):rep(-octave) + end + return numbernote[note]..octavestr ---..diffstr +end +local function makeLilyEvent(name,beatTime,note,dur) + --if note == NOP then return "" end + --local dura = math.floor(4/(dur)) + local nota = numberToNote(note) + --prerror(name,beatTime,nota,dur,dura) + return " "..nota --..dura +end +local function torational(x,maxden) + local startx = x + -- initialize matrix */ + local m = {[0]={},{}} + m[0][0] =1; m[1][1] = 1; + m[0][1] =0; m[1][0] = 0; + + -- loop finding terms until denom gets too big */ + local t + local ai = math.floor(x) + while (m[1][0] * ai + m[1][1] <= maxden) do + + t = m[0][0] * ai + m[0][1]; + m[0][1] = m[0][0]; + m[0][0] = t; + t = m[1][0] * ai + m[1][1]; + m[1][1] = m[1][0]; + m[1][0] = t; + if(x == ai) then break end -- AF: division by zero + x = 1/(x - ai); + if(x >0x7FFFFFFF) then break end -- AF: representation failure + ai = math.floor(x) + end + + -- now remaining x is between 0 and 1/ai */ + -- approx as either 0 or 1/m where m is max that will fit in maxden */ + -- first try zero */ + -- print( string.format("%d/%d, error = %e\n", m[0][0], m[1][0],startx - ( m[0][0] / m[1][0]))); + return m[0][0],m[1][0],startx - ( m[0][0] / m[1][0]) + + -- now try other possibility */ + --ai = (maxden - m[1][1]) / m[1][0]; + --m[0][0] = m[0][0] * ai + m[0][1]; + --m[1][0] = m[1][0] * ai + m[1][1]; + --print(string.format("%d/%d, error = %e\n", m[0][0], m[1][0], startx - ( m[0][0] / m[1][0]))); +end +local function getmode(sc) + local sc2 = {} + for i,v in ipairs(sc) do + sc2[i] = sc[i]-sc[1] + end + for k,v in pairs(modes) do + local bad = false + for i,n in ipairs(v) do + if n~=sc2[i] then bad=true;break end + end + if bad==false then return k end + end + return "ionian" +end +local function calcduration(score,beatLen) + local dura + if beatLen <=4 then + dura = math.floor(4/(beatLen)) + table.insert(score,dura) + else +--[[ + local rest = beatLen + if beatLen > 24 then + local laststr = score[#score] + if laststr~=">" then + while rest > 24 do + score[#score + 1] = "1*6" --24/6 + score[#score + 1] = laststr + rest = rest - 24 + end + end + end +--]] + local laststr = score[#score] + local bars = math.floor(beatLen/4) + for i=1,bars do + score[#score + 1] = "1" --24/6 + score[#score + 1] = "~"..laststr + end + local rest = beatLen - bars*4 +-------------- + if rest > 0 then + --local scale = rest/4 + --local N,M = torational(scale,10000) + --dura = "1*"..N.."/"..M + dura = math.floor(4/(rest)) + table.insert(score,dura) + else + score[#score] = nil --undo + end + end + +end +local function LILYplayEvent(self,lista,beatTime, beatLen,delta) + beatLen = math.min(beatLen,LILY.endppq - beatTime) + if beatLen <= 0 then return end + local maxlen = getmaxlen(lista) + local allkeydata = {} + local score = LILY.score[self.lilyscorenum] + local started = LILY.started[self.lilyscorenum] + if not started then + LILY.started[self.lilyscorenum] = true + if beatTime > LILY.inippq then + table.insert(score," \\key c \\minor ") + score[#score+1] = " r" + calcduration(score,beatTime - LILY.inippq) + end + end + if maxlen > 1 then + table.insert(score,"<") + end + for i=1,maxlen do + local keydata = {} + for k,v in pairs(lista) do + --need deepcopy in case item is altered in playOneEvent + --and is a table reference (ex:ctrl_function) + --keydata[k] = deepcopy(WrapAtSimple(v,i)) + keydata[k] = WrapAtSimple(v,i) + end + + local note + if keydata.freq then + note = freq2midi(keydata.freq) + elseif keydata.note then + note = keydata.note + elseif keydata.degree then + note = getNote(keydata.degree,keydata.escale or "ionian") + end + keydata.note = note + --keep ranges + if note~=REST and note~=NOP then + local minnote = LILY.minnote[self.lilyscorenum] or math.huge + local maxnote = LILY.maxnote[self.lilyscorenum] or -math.huge + minnote = (note < minnote ) and note or minnote + maxnote = (note > maxnote ) and note or maxnote + LILY.minnote[self.lilyscorenum] = minnote + LILY.maxnote[self.lilyscorenum] = maxnote + end + --armadura + if keydata.escale then + local tonic = keydata.escale[1] + if LILY.tonics[self.lilyscorenum]~=tonic then + LILY.tonics[self.lilyscorenum]=tonic + local key = notenumbers[tonic%12] + table.insert(score," \\key "..key.." \\"..getmode(keydata.escale).." ") + end + end + --if note == NOP then return end + local str = makeLilyEvent(self.name,beatTime,note,keydata.dur) + table.insert(score,str) + allkeydata[i] = keydata + end + if maxlen > 1 then + table.insert(score,">") + end + calcduration(score,beatLen) + + --print("dur",beatLen,dura) +end + +function LILY:open(filepath) + local file,err = io.open(filepath,"wb") + self.file = file + if not self.file then error(err) end + self.filepath = filepath + +end +function LILY:sendBundle(msg,time) + +end +function LILY:sendMultiBundle(time,msg) + +end +local osctable = {} +local clefs = {[0]="treble",[1]="treble^8",[2]="treble^15",[-1]="bass",[-2]="bass_8",[-3]="bass_15"} +function LILY:SaveStr(file) + local fich,err=io.open(file,"wb") + if not fich then error(err) end + fich:write[[\version "2.18.2"]] + fich:write[[\layout{\context{\Voice +\override Beam.breakable = ##t +\remove "Forbid_line_break_engraver" +\remove "Note_heads_engraver" +\consists "Completion_heads_engraver" +}} ]] + fich:write("<<") + for i=1,#self.players do + local pl = self.players[i] + local minn = self.minnote[pl.lilyscorenum] + local maxn = self.maxnote[pl.lilyscorenum] + local midn = (minn + maxn)*0.5 + local clindex = math.floor(0.5+((midn - 67)/12)) + clindex = clip(clindex,-3,2) + local clef = clefs[clindex] + table.insert(self.score[i],2,"\\clef \""..clef.."\" ") + print(pl.name,clef,midn,clindex,self.minnote[pl.lilyscorenum],self.maxnote[pl.lilyscorenum]) + fich:write(table.concat(self.score[i])) + end + fich:write(">>") + fich:close() +end + +function LILY:close() + self.file:close() + self.closed = true +end +function LILY:Gen(inippq,endppq,players) + print"LILY:Gen" + self.inippq = inippq + self.endppq = endppq + self.score = {} + self.started = {} + self.players = players + self.tonics = {} + self.minnote = {} + self.maxnote = {} + for i=1,#players do + players[i].name = players[i].name or players[i]:findMyName() + local staftype = players[i].isOscPianoEP and "PianoStaff" or "Staff" +--%\remove "Note_heads_engraver" +--%\consists "Completion_heads_engraver" +--%\remove "Rest_engraver" +--%\consists "Completion_rest_engraver" +--%\consists "Rhythmic_column_engraver" + self.score[i] = {string.format("\n\\new %s \\with{instrumentName = #%q } {",staftype,players[i].name)} + players[i].lilyscorenum = i + end + self.test = test + local function pathnoext(P) + return P:match("([^%.]+)") + end + + theMetro:play(nil,0,0,25) + local lastt = 0 + sendBundle = function(msg,ti) + end + sendMultiBundle = function(ti,msg) + end + sendBlocked = function(msg) + return {"/done",{}} + end + EventPlayer.playEvent = LILYplayEvent + curHostTime = theMetro + _initCb() + -- table.insert(initCbCallbacks,function() + print"LILYPOND work" + theMetro:play(nil,inippq,0,25) + theMetro.oldtimestamp = -theMetro.period + while theMetro.ppqPos < endppq do + theMetro.timestamp = theMetro.oldtimestamp + theMetro.period + theMetro.oldppqPos = theMetro.ppqPos + theMetro.ppqPos = theMetro.ppqPos + theMetro.frame + --print("theMetro",theMetro.oldppqPos,theMetro.ppqPos) + --_onFrameCb() + for i,v in ipairs(players) do + --println("onframe player:",v.name) + --v:Play() + EventPlayer.Play(v) + end + theMetro.oldtimestamp = theMetro.timestamp + end + + print"saving osc table" + for i=1,#players do + local score = self.score[i] + score[#score+1] = "}" + end + local lilyfile = pathnoext(scriptname)..".ly" + local pdffile = pathnoext(scriptname)..".pdf" + self:SaveStr(lilyfile) + print"osc table saved" + --require"lfs" + --print(lfs.currentdir()) + --os.execute([[C:\Program Files\LilyPond\usr\bin\lilypond.exe -fpdf ]].. lilyfile) + os.remove(pdffile) + os.execute(string.format([["C:\Program Files (x86)\LilyPond\usr\bin\lilypond" -V -fpdf -o%s %s]],pathnoext(scriptname),lilyfile)) + print"pdf done" + --os.execute(pathnoext(scriptname)..".pdf") + io.popen(pdffile) + --end) +end +return LILY \ No newline at end of file diff --git a/lua2SC/lua/sc/miditoosc.lua b/lua2SC/lua/sc/miditoosc.lua index 8d859e8..f4efea2 100644 --- a/lua2SC/lua/sc/miditoosc.lua +++ b/lua2SC/lua/sc/miditoosc.lua @@ -196,7 +196,10 @@ function InstrumentsGUI(synname,chooser,parent,params,notified) value=1, type=GUITypes.onOffButton, FormatLabel=function() return "Release" end, - callback=function(val,str,c) self.oscfree =(val==1) end + callback=function(val,str,c) + self.oscfree =(val==1) + --if self.free_queue + end } addControl{panel=panelbuttons,type=GUITypes.kickButton,label="Clipboard", callback=function(val) @@ -300,6 +303,7 @@ end --midiin to out osc MidiToOsc={ nodesMidi2Osc={}, + free_queue = {}, vars={} } @@ -310,7 +314,7 @@ function Midi2OSCEnvio(ch,fx,lev) table.insert(MidiToOsc.vars[ch].envios,{node=node,level=lev}) msg ={"/s_new", {"envio", node, 1, MidiToOsc.vars[ch].group,"busin",{"int32",MidiToOsc.vars[ch].channel.busin},"busout",{"int32",fx.channel.busin},"level",{"float",lev}}} --prtable(msg) - udp:send(toOSC(msg)) + sendBundle(msg) end function iguiSendLevel(self,i,lev) self.envios[i].level = lev @@ -361,6 +365,8 @@ function MidiToOsc.AddChannel(ch,igui,sends,on_maker,inserts,mono) igui.notify = iguinotify MidiToOsc.nodesMidi2Osc[ch] = {} igui.nodes = MidiToOsc.nodesMidi2Osc[ch] + MidiToOsc.free_queue[ch] = {} + igui.free_queue = MidiToOsc.free_queue[ch] igui.inserts=inserts or {} MidiToOsc.vars[ch].on_maker = on_maker @@ -375,10 +381,10 @@ function MidiToOsc.Init(ch) print"Miditooscinit" MidiToOsc.vars[ch].group = GetNode() local msg={NEW_GROUP,{MidiToOsc.vars[ch].group,0,0}} - udp:send(toOSC(msg)) + sendBundle(msg) MidiToOsc.vars[ch].instr_group = GetNode() msg={"/p_new",{MidiToOsc.vars[ch].instr_group,0,MidiToOsc.vars[ch].group}} - udp:send(toOSC(msg)) + sendBundle(msg) --prtable(MidiToOsc.vars[ch].channel) MidiToOsc.vars[ch].channel=CHN(MidiToOsc.vars[ch].channel or {},MidiToOsc.vars[ch]) -----inserts @@ -393,12 +399,12 @@ function MidiToOsc.Init(ch) self._inserts={} for i,insert in ipairs(self.inserts) do - print"xxxxxxxxxxxxxxcreo ins" + print"miditoosc creo ins" self._inserts[i]=INS(insert,self,true) end ------------------------------------ for i2,v2 in ipairs(Effects) do - print"xxxxxxxxxxxxxxcreo send" + print"miditoosc creo send" Midi2OSCEnvio(ch,v2,MidiToOsc.vars[ch].sends[i2] or 0) end @@ -440,8 +446,11 @@ function MidiToOsc.midi2osc(midiEvent) --thisMidiOsc.keylist = thisMidiOsc.keylist or {} table.insert(thisMidiOsc.keylist,midiEvent.byte2) else - nodo = GetNode() - snew = true + nodo = MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] + if not nodo then + nodo = GetNode() + snew = true + end end local freq = midi2freq(midiEvent.byte2) local amp = midiEvent.byte3/127.0 @@ -455,6 +464,10 @@ function MidiToOsc.midi2osc(midiEvent) else on ={"/s_new", {thisMidiOsc.inst, nodo, 0, thisMidiOsc.instr_group, "freq", {"float" ,freq},"amp",{"float",amp}}} end + MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2]=nodo + OSCFunc.newfilter("/n_end",nodo,function(noty) + MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2]=nil + end,true) else if thisMidiOsc.on_maker then on ={"/n_set", {nodo}} @@ -462,21 +475,23 @@ function MidiToOsc.midi2osc(midiEvent) else on ={"/n_set", { nodo, "freq", {"float" ,freq},"amp",{"float",amp}}} end + MidiToOsc.free_queue[ch][nodo] = nil end ValsToOsc(on[2],thisMidiOsc.params) table.insert(on[2],"out") table.insert(on[2],{"int32",thisMidiOsc.channel.busin}) - --to avoid repeating note - if not MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] then - sendBundle(on) --,lanes.now_secs()) - MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2]=nodo - end + sendBundle(on) + + --if not MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] then + --sendBundle(on) --,lanes.now_secs()) + --MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2]=nodo + --end elseif midiEvent.type==midi.noteOff then - local nodo = MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] - MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] = nil if mono then + local nodo = MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] + MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] = nil for i,v in ipairs(thisMidiOsc.keylist) do if v == midiEvent.byte2 then table.remove(thisMidiOsc.keylist, i) @@ -496,12 +511,17 @@ function MidiToOsc.midi2osc(midiEvent) end elseif thisMidiOsc.oscfree then + local nodo = MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] + MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] = nil if nodo then local off = {"/n_set",{nodo,"gate",{"float",0}}} sendBundle(off) --,lanes.now_secs()) --local off = {"/n_free",{nodo}} --udp:send(toOSC(off)) end + else --poly dontfree + local nodo = MidiToOsc.nodesMidi2Osc[midiEvent.channel][midiEvent.byte2] + if nodo then MidiToOsc.free_queue[ch][nodo] = true end end else --sendMidi(midiEvent) diff --git a/lua2SC/lua/sc/named_events.lua b/lua2SC/lua/sc/named_events.lua index 5d81ad2..0694852 100644 --- a/lua2SC/lua/sc/named_events.lua +++ b/lua2SC/lua/sc/named_events.lua @@ -56,7 +56,7 @@ function WAITEv(name) e.prevppqPos = -math.huge --avoid reset e.ppqPos = ppq; e.playing = true - e:Play() + --e:Play() end,e) return {dur=1,delta=math.huge,freq=NOP} @@ -117,17 +117,17 @@ local function genUntilev(name,waitmark) if named_events:is_set(name) then if waitmark then if val._mark then - --print("UNTILEV returns mark nil",lanes.now_secs()) + --print("UNTILEV returns mark nil",e.name) return nil end else - --print("UNTILEV returns nil",lanes.now_secs()) + --print("UNTILEV returns nil",e.name) return nil end end --print("UNTILEV not setted",val.degree,lanes.now_secs()) if val == nil then finished = true end - if finished then return {dur=1,delta=math.huge,freq=NOP} end + if finished then return {dur=1,delta=math.huge,freq=NOP} end --NOP return val end local function rfunc() diff --git a/lua2SC/lua/sc/playersppq.lua b/lua2SC/lua/sc/playersppq.lua index bdefbe5..5369452 100644 --- a/lua2SC/lua/sc/playersppq.lua +++ b/lua2SC/lua/sc/playersppq.lua @@ -278,7 +278,7 @@ function EventPlayer:Play() --end self:Pull() - while self.ppqPos < curHostTime.ppqPos and curHostTime.oldppqPos <= self.ppqPos do + while curHostTime.oldppqPos <= self.ppqPos and self.ppqPos < curHostTime.ppqPos do local havenext = self:NextVals() if havenext == nil then if self.playing then @@ -293,6 +293,7 @@ function EventPlayer:Play() self.playing = false break else + --print("play",self.name,self.ppqPos,curHostTime.oldppqPos,curHostTime.ppqPos) self:playEvent(self.curlist,self.ppqPos,self.curlist.dur,self.curlist.delta) self:UpdatePos(self.curlist.delta) end @@ -586,18 +587,7 @@ function ActionEventPlayer:Reset() self.playing= (self.doplay==nil) and true or self.doplay self.used=false end -function ActionEventPlayer:playEventBAK(lista,beatTime, beatLen) - print("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxplay action ",beatTime,curHostTime.ppqPos) - local v=lista.actions - --for i,v in ipairs(lista.actions) do - --if lista.actions.type == "unpack" then - v[1](unpack(v[2])) - --prtable(v) - --else - --v[1](v[2]) - --end - --end -end + function ActionEventPlayer:playEvent(lista,beatTime, beatLen) print("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx play action ",beatTime,self.ppqPos) local v=lista.actions diff --git a/lua2SC/lua/sc/playerssc.lua b/lua2SC/lua/sc/playerssc.lua index dc0a7d4..b2fc450 100644 --- a/lua2SC/lua/sc/playerssc.lua +++ b/lua2SC/lua/sc/playerssc.lua @@ -4,7 +4,7 @@ require"sc.sc_comm" NEW_GROUP = "/g_new" --"/p_new" -- "/g_new" -------------------------------------------------------- OsceventQueue = {} -OsceventQueueDirty = false +local OsceventQueueDirty = false function OsceventCompare(a, b) return b.time>a.time end @@ -99,6 +99,7 @@ function scEventPlayer:Release(time) end function scEventPlayer:FreeNode() --print("Freenode",self.name,self.node) + if self.poly and self.NodeQueue then for i,v in ipairs(self.NodeQueue) do local msg = {"/n_set",{v,"gate",{"float",0}}} @@ -106,9 +107,14 @@ function scEventPlayer:FreeNode() end return end - if self.node == nil then return end + if self.node == nil then print("Freenode without node",self.name);return end +-- if self.name=="om1" then +-- print("om1 in",self.ppqPos) +-- OSCFunc.newfilter("/n_info",self.node,function(v) prtable(v) end,true) +-- sendBundle({"/n_query",{self.node}},theMetro:ppq2time(self.ppqPos)) +-- end local msg = {"/n_set",{self.node,"gate",{"float",0}}} - sendBundle(msg) --,lanes.now_secs()) + sendBundle(msg,theMetro:ppq2time(self.ppqPos)) --,lanes.now_secs()) self.node = nil end function scEventPlayer:playOneEvent(lista,beatTime, beatLen) @@ -118,6 +124,13 @@ function scEventPlayer:playOneEvent(lista,beatTime, beatLen) lista.dur=nil --local inst=lista.inst lista.inst=nil + --eval funcs + for k,v in pairs(lista) do + if type(v)=="function" then + lista[k]=v(self) + end + end + --play osc----------------- local msg if self.node == nil then @@ -174,20 +187,20 @@ function scEventPlayer:playOneEvent(lista,beatTime, beatLen) end end -function scEventPlayer:SendParam(parnam) +function scEventPlayer:SendParam(parnam,ti) --assert(self.node," sin nodo") if not self.node then return end --local msg ={"/n_set", { self.node,parnam,{"float",self.params[parnam]}}} local msg ={"/n_set", { self.node}} msg = getMsgValue(msg,parnam,self.params[parnam]) - sendBundle(msg) --,lanes.now_secs()) + sendBundle(msg,ti) --,lanes.now_secs()) end -function scEventPlayer:SendParams() +function scEventPlayer:SendParams(ti) --if not self.node then print("scEventPlayer:SendParams ",self.name, " sin nodo"); return end assert(self.node," sin nodo") local msg ={"/n_set", { self.node}} msg = getMsgLista(msg,self.params) - sendBundle(msg) --,lanes.now_secs()) + sendBundle(msg,ti) --,lanes.now_secs()) self:sendToRegisteredControls() end function scEventPlayer:RegisterControl(control) @@ -487,24 +500,10 @@ function OscEventPlayer:Reset(all) insert:Reset() end end - EventPlayer.Reset(self) self:FreeNode() + EventPlayer.Reset(self) end ---called once a frame -function OscEventPlayer:ccPlayBAK() - local event - if self.group and self.cclist then - --if self.node then - event ={"/n_set", {self.group}} - for k,v in pairs(self.cclist) do - local val=v:nextval() - table.insert(event[2],k) - table.insert(event[2],{"float",val}) - end - --print(event) - sendBundle(event) - end -end + function OscEventPlayer:Play() --print("OscEventPlayer:Play",self.name) self.channel:Play() @@ -553,12 +552,13 @@ local function copylist(lis) end return res end -function OscEventPlayer:playOneEvent(listaO,beatTime, beatLen,delta) +function OscEventPlayer:playOneEvent(lista,beatTime, beatLen,delta) + if self.piano then return self:playOnePianoEvent(lista,beatTime, beatLen,delta) end --set defaults, get freq,escale,legato,inst - local lista=listaO --deepcopy(listaO) lista.escale = lista.escale or "ionian" local escale = lista.escale + --because lista will be changed self.curvals = copylist(lista) --eval funcs @@ -567,9 +567,6 @@ function OscEventPlayer:playOneEvent(listaO,beatTime, beatLen,delta) lista[k]=v(self) end end - - -------------- - --freq local freq @@ -594,11 +591,8 @@ function OscEventPlayer:playOneEvent(listaO,beatTime, beatLen,delta) local inst = lista.inst or self.inst lista.inst=nil - --lista.amp = lista.amp or lista.velo;lista.velo=nil - --lista.amp = lista.amp or 0.5 if IsREST(freq) then - --self.havenode = false self:Release(theMetro:ppq2time(beatTime)) return end @@ -612,6 +606,7 @@ function OscEventPlayer:playOneEvent(listaO,beatTime, beatLen,delta) local dontfree = self.dontfree if self.mono then + --if self.node then if self.havenode then lista.type = "n_set" end @@ -655,11 +650,6 @@ function OscEventPlayer:playOneEvent(listaO,beatTime, beatLen,delta) table.insert(on[2],"out") table.insert(on[2],{"int32",self.channel.busin}) --- if freq then --- table.insert(on[2],"freq") --- table.insert(on[2],{"float",freq}) --- end - --sendBundle(on,theMetro:ppq2time(beatTime)) --send functions local gbundle = {on} for k,v in pairs(listafunc) do @@ -682,15 +672,184 @@ function OscEventPlayer:playOneEvent(listaO,beatTime, beatLen,delta) if dontfree == false then local off = {"/n_set",{self.node,"gate",{"float",0}}} scheduleOscEvent(off,beatTime + beatLen) - --if self.mono then - -- self.node = nil - --end - --if not self.monogate then - --self.node = nil - --end self.havenode = false + --self.node = nil end end +------------------------------------------------ +------------------------------OscPianoEventPlayer-------------- +OscPianoEventPlayer = OscEventPlayer:new({isOscPianoEP=true}) +function OscPianoEP(t) + local player = OscPianoEventPlayer:new(t) + player.params={} + player.pedalqueue = {} + player.pianofreequeue = {} + player.pianonodes = {} + OSCPlayers[#OSCPlayers + 1]= player + return player +end +--function OscPianoEventPlayer:Play() +-- +-- self:doFreeQueue(self.ppqPos) +-- if not self.pianopedal then +-- self:doPedalQueue() +-- end +-- OscEventPlayer.Play(self) +--end +function OscPianoEventPlayer:setFreeQueue(node,time,freq) + table.insert(self.pianofreequeue,{time=time,node=node,freq=freq}) + self.freeQueueDirty = true +end +function OscPianoEventPlayer:doFreeQueue() + local ppq = self.ppqPos + + --ensure the table is in order + if self.freeQueueDirty then + table.sort(self.pianofreequeue, function(a,b) return b.time>a.time end) + self.freeQueueDirty = false + end + + --send all events in this window + local pianofreequeue = self.pianofreequeue + repeat + if pianofreequeue[1] and pianofreequeue[1].time <= ppq then + if self.pianopedal then + self.pedalqueue[pianofreequeue[1].node] = pianofreequeue[1] + elseif self.pianonodes[pianofreequeue[1].freq] then + local off = {"/n_set",{pianofreequeue[1].node,"gate",{"float",0}}} + sendBundle(off, theMetro:ppq2time(pianofreequeue[1].time)) + self.pianonodes[pianofreequeue[1].freq] = nil + self.pedalqueue[pianofreequeue[1].node] = nil + end + table.remove(pianofreequeue, 1) + else + break + end + until false + if not self.pianopedal then + self:doPedalQueue() + end +end +function OscPianoEventPlayer:doPedalQueue() + for i,v in pairs(self.pedalqueue) do + local off = {"/n_set",{v.node,"gate",{"float",0}}} + sendBundle(off, theMetro:ppq2time(self.ppqPos)) + self.pianonodes[v.freq] = nil + end + self.pedalqueue = {} +end +function OscPianoEventPlayer:playOneEvent(lista,beatTime, beatLen,delta) + + --set defaults, get freq,escale,legato,inst + lista.escale = lista.escale or "ionian" + local escale = lista.escale + --because lista will be changed + self.curvals = copylist(lista) + + --eval funcs + for k,v in pairs(lista) do + if type(v)=="function" then + lista[k]=v(self) + end + end + + --freq + local freq + if lista.freq then + freq = lista.freq + elseif lista.note then + --freq = functabla(lista.note,midi2freq) + freq = midi2freq(lista.note) + elseif lista.degree then + freq = midi2freq(getNote(lista.degree,escale)) + --freq = functabla(freq,midi2freq) + end + lista.note=nil;lista.degree=nil;lista.freq=freq + + --legato + local legato + if lista.legato then + beatLen = beatLen * lista.legato;legato=lista.legato;lista.legato=nil + end + + --inst = lista.inst or "default"; + local inst = lista.inst or self.inst + lista.inst=nil + + if lista.pianopedal~=nil then + self.pianopedal = lista.pianopedal + --prerror("self.pianopedal",self.pianopedal) + --if self.pianopedal then + --self:doFreeQueue() + --end + end + self:doFreeQueue() + lista.pianopedal = nil + + if IsREST(freq) then + --self:Release(theMetro:ppq2time(beatTime)) + return + end + if IsNOP(freq) then + return + end + + local thisnode,on + if not freq then + else + thisnode = self.pianonodes[freq] + if thisnode then + on ={"/n_set", {thisnode}} + else + thisnode = GetNode() --self:GetNode(beatTime) + self.pianonodes[freq] = thisnode + on ={"/s_new", {inst, thisnode, 0, self.instr_group}} + OSCFunc.newfilter("/n_end",thisnode,function(noty) + self.pianonodes[freq] = nil + self.pedalqueue[thisnode] = nil + end,true) + end + end + + -- get is_ctrl_mapper + local listafunc = {} + for k,v in pairs(lista) do + --if type(v)=="function" then + if type(v)=="table" and v.is_ctrl_mapper then + listafunc[k]=v + lista[k]=nil + end + end + + lista.escale=nil --dont send escale + getMsgLista(on,lista) + lista.escale=escale + + table.insert(on[2],"out") + table.insert(on[2],{"int32",self.channel.busin}) + + --send functions + local gbundle = {on} + for k,v in pairs(listafunc) do + error"not implemented" + local bund = v:verb(k,self,beatTime,beatLen) + if bund then + for i,vv in ipairs(bund) do table.insert(gbundle,vv) end + end + end + sendMultiBundle(theMetro:ppq2time(beatTime),gbundle) + --- + if _GUIAUTOMATE then + self.autom_dur=self.autom_dur+ beatLen + if self.autom_dur >= 0.5 then + self.autom_dur=0 + self.params = lista + self:sendToRegisteredControls() + end + end + + self:setFreeQueue(thisnode,beatTime + beatLen,freq) +end --------------------------------------Inicio function FillSends(val) local res={} diff --git a/lua2SC/lua/sc/sc_comm.lua b/lua2SC/lua/sc/sc_comm.lua index ef0906c..0ce3a61 100644 --- a/lua2SC/lua/sc/sc_comm.lua +++ b/lua2SC/lua/sc/sc_comm.lua @@ -1,6 +1,5 @@ --- udp comunication ---require"init.init" -require("socket") --.core") +require("socket") require("osclua") require("sc.number2string") toOSC=osclua.toOSC diff --git a/lua2SC/lua/sc/scales.lua b/lua2SC/lua/sc/scales.lua index 678d2c5..bb52df6 100644 --- a/lua2SC/lua/sc/scales.lua +++ b/lua2SC/lua/sc/scales.lua @@ -18,7 +18,7 @@ end modes = { ionian = constructMode(0), dorian = constructMode(1), -phyrgian = constructMode(2), +phrygian = constructMode(2), lydian = constructMode(3), mixolydian = constructMode(4), aeolian = constructMode(5), diff --git a/lua2SC/lua/sc/stream.lua b/lua2SC/lua/sc/stream.lua index 5abbcfd..17cb84f 100644 --- a/lua2SC/lua/sc/stream.lua +++ b/lua2SC/lua/sc/stream.lua @@ -72,7 +72,7 @@ function Stream:reset() self.creps = 0 self.recur = nil if self.repSt then - print"TODO: this is not a complete reset of the outer stream" + --print"TODO: this is not a complete reset of the outer stream" self.reps = self.repSt:nextval() end end @@ -593,6 +593,19 @@ function DIV(A,B) return OpStreams:new({stA=A,stB=B,Op=divOp}) end --------pairs streams---------------------------------------------- +local event_mt = {} +function event_mt.__add(a,b) + local res = {} + for k,v in pairs(a) do + if b[k] then + res[k] = v + b[k] + else + res[k] = v + end + end + return setmetatable(res,event_mt) +end + PairsStream = Stream:new{stlist=nil} PairsStream.isPairsStream=true function PairsStream:pnext(e) @@ -626,7 +639,7 @@ function PairsStream:pnext(e) end list.delta = list.delta or list.dur --e.tmplist = nil - return list + return setmetatable(list,event_mt) end function PairsStream:reset() for i,t in ipairs(self.stlist) do @@ -678,8 +691,10 @@ function AdvStream:pnext(e) if self.N2 and self.N2.advance and self.N1 then local advance = self.N2.advance self.N2.advance = nil - self.N1.delta = math.max(0,self.N1.delta + advance) - self.N2.delta = math.max(0,self.N2.delta - advance) + local newn1delta = math.max(0,self.N1.delta + advance) + local newadvance = newn1delta - self.N1.delta + self.N1.delta = newn1delta + self.N2.delta = math.max(0,self.N2.delta - newadvance) self.N1.dur = math.max(0,self.N1.dur + advance) self.N2.dur = math.max(0,self.N2.dur - advance) --print("advance",advance) @@ -769,6 +784,8 @@ function ParalelStream:reset() Stream.reset(self) end function ParS(t) + --assert(#t > 0,"no tables in ParS, use { and not (") + if #t < 1 then error("no tables in ParS, use { and not (",2) end return ParalelStream:new{stlist=t} end ------------Merge stream @@ -816,14 +833,26 @@ end -- gets a value from pat and repeats n times RepeaterSt = Stream:new{} function RepeaterSt:pnext(e) - local n = self.N:nextval(e) - local val = self.pat:nextval(e) + local val + if not self.privateL then + local n = self.N:nextval(e) + local val2 = self.pat:nextval(e) + if not val2 then return nil end + self.privateL = LS({val2},n) + end + val = self.privateL:nextval() if val then - return LS({val},n) + return val else - return nil + self.privateL = nil + return self:pnext(e) end end +function RepeaterSt:reset() + self.N:reset() + self.pat:reset() + self.privateL = nil +end function REP(n,pat) ---[[ if type(n) ~="table" or not n.isStream then --no es stream @@ -1080,4 +1109,35 @@ function COUNT(str) local turn = 0 PS{dur =LS{0},_dummy=function() turn=turn+1;print(str,turn) end} end +--used for seting inserts from player stream +function SETINS(i,parvals) + return FS(function(pl) + local function fun(pll) + for k,v in pairs(parvals) do + pl._inserts[i].params[k]=v + end + pl._inserts[i]:SendParams(theMetro:ppq2time(pll.ppqPos)) + end + return {delta=0,dur=0,freq=NOP,_=fun} + end) +end +--used for seting channel from player stream +function SETCHAN(parvals) + return FS(function(pl) + local beatLen = parvals.dur + parvals.dur = nil + local function fun(pll) + for k,v in pairs(parvals) do + if not (type(v)=="table" and v.is_ctrl_mapper) then + pl.channel.params[k]=v + else + local bund = v:verb(k,pl.channel,pll.ppqPos,beatLen) + sendMultiBundle(theMetro:ppq2time(pll.ppqPos),bund) + end + end + pl.channel:SendParams(theMetro:ppq2time(pll.ppqPos)) + end + return {delta=0,dur=0,freq=NOP,_=fun} + end) +end ------------------- \ No newline at end of file diff --git a/lua2SC/lua/sc/synthdefsc.lua b/lua2SC/lua/sc/synthdefsc.lua index 32d61a5..145005e 100644 --- a/lua2SC/lua/sc/synthdefsc.lua +++ b/lua2SC/lua/sc/synthdefsc.lua @@ -432,7 +432,6 @@ UGen.__add=function(a,b) return BinaryOpUGen.newop('+',a,b) end UGen.__sub=function(a,b) - print"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzsoy sub" if b == 0 then return a end if a == 0 then return -b end return BinaryOpUGen.newop('-',a,b) @@ -490,7 +489,6 @@ function UGen:madd(mul,add) return self + add else --return self * mul + add - print("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz MULADD") return MulAdd:newmuladd(self,mul,add) end end @@ -1393,7 +1391,6 @@ function Mix(t) if t.isUGen then return t end if isSimpleTable(t) then t = UGenArr:new(t) end - print("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxMIX") --prtable(t) if(#t<=1) then print"mixing less than two chanels" end local reducedArray = t:clump(4) diff --git a/lua2SC/lua2sc.lua b/lua2SC/lua2sc.lua index c972be8..405ad19 100644 --- a/lua2SC/lua2sc.lua +++ b/lua2SC/lua2sc.lua @@ -122,13 +122,25 @@ function thread_error_print(...) idlelinda:send("prout",{strconcat(...):sub(1,30000),true}) end function MidiOpen(options) - midilane = pmidi.gen(options.midiin, options.midiout, lanes ,mainlinda,midilinda,{print=thread_print, + midilane = pmidi.gen(options.midiin, options.midiout, lanes ,mainlinda,midilinda,{ + print=thread_print, prerror=thread_error_print, - prtable=prtable,idlelinda = idlelinda}) + --prerror = print, + prtable=prtable,idlelinda=idlelinda}) end function MidiClose() - if midilane then - midilane:cancel(0.1) + --if midilane then + -- midilane:cancel(0.1) + --end + if midilane then + local status = midilane.status + if status == "pending" or status == "running" or status == "waiting" then + midilinda:send("exit_midi_thread",1) + io.write"waiting exit_midi_thread_done\n" + mainlinda:receive("exit_midi_thread_done") + io.write"received exit_midi_thread_done\n" + end + midilane = nil end end --require"sc.oscfunc" @@ -173,6 +185,7 @@ while true do idlelinda:send("_midiEventCb",val) elseif key == "MidiClose" then MidiClose() + val:send("MidiClose_done",1) elseif key == "MidiOpen" then MidiOpen(val) elseif key == "ScriptRun" then diff --git a/luaportmidi/pmidi.lua b/luaportmidi/pmidi.lua index 821c27b..1bc615b 100644 --- a/luaportmidi/pmidi.lua +++ b/luaportmidi/pmidi.lua @@ -16,10 +16,10 @@ M.OpenOutput=mm.OpenOutput function M._sendMidi(ev) midilinda:send("midiWriteShort",ev) end -function M.exit_midi_thread() - midilinda:send("exit_midi_thread",1) - midilinda:receive( "exit_midi_thread_done" ) -end +-- function M.exit_midi_thread() + -- midilinda:send("exit_midi_thread",1) + -- midilinda:receive( "exit_midi_thread_done" ) +-- end function M.GetMidiDevices() local devices=mm.listDevices() local res={inp={},out={}} @@ -80,15 +80,8 @@ function M.midi_thread(inp,out) end print("PMIDI: midi_thread finalizer beguin") lanes.timer(midilinda, "miditimer", 0 ) --reset - midilinda:receive (0, "miditimer" ) --clear last - print("PMIDI: midi_thread closing devices") - for i,v in ipairs( midiout) do - v.stream:close() - end - for i,v in ipairs( midiin) do - v.stream:close() - end - print("PMIDI: midi_thread closed devices") + midilinda:set ("miditimer" ) --clear last + print("PMIDI: midi_thread finalizer end") end) set_error_reporting("extended") set_debug_threadname("midi_thread") @@ -102,7 +95,7 @@ function M.midi_thread(inp,out) if not mdin then print("PMIDI: "..tostring(err)) else - midiin[#midiin+1]={stream=mdin,port=MIDIdev.inp[name].devID} + midiin[#midiin+1]={stream=mdin,port=MIDIdev.inp[name].devID,name=name} print("PMIDI: midiin: ", #midiin, " opened:",name) end end @@ -115,7 +108,7 @@ function M.midi_thread(inp,out) print("PMIDI: "..tostring(err)) else midi_out_opened=true - midiout[#midiout+1]={stream=mdin,port=MIDIdev.out[name].devID} + midiout[#midiout+1]={stream=mdin,port=MIDIdev.out[name].devID,name=name} print("PMIDI: midiout: ",#midiout," opened:",name) end end @@ -124,7 +117,7 @@ function M.midi_thread(inp,out) if #midiin > 0 or midi_out_opened then lanes.timer( midilinda, "miditimer", 0.01, 0) --0.01 ) while(true) do - local key,val= midilinda:receive( "miditimer","midiWriteShort","exit_midi_thread" ) + local key,val= midilinda:receive("exit_midi_thread", "miditimer","midiWriteShort" ) --if key=="miditimer" and #midiin > 0 then if key=="miditimer" then @@ -154,8 +147,18 @@ function M.midi_thread(inp,out) --prtable(val) end elseif key=="exit_midi_thread" then - midilinda:send("exit_midi_thread_done",1) - --print(key,val) + io.write"PMIDI: exit_midi_thread arrived" + print("PMIDI: midi_thread closing devices") + for i,v in ipairs( midiout) do + print("closing ",v.name) + v.stream:close() + end + for i,v in ipairs( midiin) do + print("closing ",v.name) + v.stream:close() + end + print("PMIDI: midi_thread closed devices") + callbacklinda:send("exit_midi_thread_done",1) break end end