aboutsummaryrefslogtreecommitdiffstats
path: root/aataaj.lua
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xaataaj.lua262
1 files changed, 75 insertions, 187 deletions
diff --git a/aataaj.lua b/aataaj.lua
index 761163e..a92e0fb 100755
--- a/aataaj.lua
+++ b/aataaj.lua
@@ -14,6 +14,17 @@
14### END INIT INFO 14### END INIT INFO
15]] 15]]
16 16
17local _ = require '_'
18local D = _.D
19local I = _.I
20local T = _.T
21local W = _.W
22local E = _.E
23local C = _.C
24local __ = _._
25
26
27
17local Help = [[ 28local Help = [[
18This is part of the AllAudioToALSAandJACK project, aataaj for short, 29This is part of the AllAudioToALSAandJACK project, aataaj for short,
19pronounced like "attach". 30pronounced like "attach".
@@ -40,14 +51,12 @@ The packages you need installed are -
40 51
41You need to have the snd-aloop kernel module loaded. 52You need to have the snd-aloop kernel module loaded.
42 53
43The aataaj.lua script should be run at boot time, put it into 54The aataaj.lua script should be run at boot time, put it and _.lua into
44/etc/init.d/ and activate it with - 55/etc/init.d/ and activate it with -
45 56
46update-rc.d aataaj.lua defaults 57update-rc.d aataaj.lua defaults
47 58
48It scans for your sound devices and creates /var/lib/aataaj/asoundrc. 59Note that _.lua might need to be in /usr/local/share/lua/5.1/
49You can run it manually with "aataaj start" each time you need to change
50your devices.
51 60
52 61
53 62
@@ -83,131 +92,6 @@ NOTE - Seems both ALSA and JACK are per user. So you need to run
83 92
84 93
85 94
86-- Most of this APT stuff was copied from apt-panopticon.
87local APT = {}
88
89APT.readCmd = function(cmd)
90 local result = {}
91 local output = io.popen(cmd)
92 if nil ~= output then
93 for l in output:lines() do
94 table.insert(result, l)
95 end
96 end
97 -- While this does return the same things as os.execute(), it's just as useless.
98 output:close()
99 return result
100end
101
102APT.exe = function(c)
103 local exe = {status = 0, lines = {}, log = true, cmd = c .. ' ', command = c}
104
105 function exe:log()
106 self.log = true
107 return self
108 end
109 function exe:Nice(c)
110 if nil == c then
111 self.cmd = 'ionice -c3 nice -n 19 ' .. self.cmd
112 else
113 self.cmd = self.cmd .. 'ionice -c3 nice -n 19 ' .. c .. ' '
114 end
115 return self
116 end
117 function exe:timeout(c)
118 -- timeout returns a status of - command status if --preserve-status; "128+9" (actually 137) if --kill-after ends up being done; 124 if it had to TERM; command status if all went well.
119 -- --kill-after means "send KILL after TERM fails.
120 if nil == c then
121 self.cmd = 'timeout --kill-after=10.0 --foreground 42.0s ' .. self.cmd
122 else
123 self.cmd = 'timeout --kill-after=10.0 --foreground ' .. c .. ' ' .. self.cmd
124 end
125 return self
126 end
127 function exe:also(c)
128 if nil == c then c = '' else c = ' ' .. c end
129 self.cmd = self.cmd .. ';' .. c .. ' '
130 return self
131 end
132 function exe:And(c)
133 if nil == c then c = '' else c = ' ' .. c end
134 self.cmd = self.cmd .. '&&' .. c .. ' '
135 return self
136 end
137 function exe:Or(c)
138 if nil == c then c = '' end
139 self.cmd = self.cmd .. '|| ' .. c .. ' '
140 return self
141 end
142 function exe:noErr()
143 self.cmd = self.cmd .. '2>/dev/null '
144 return self
145 end
146 function exe:wait(w)
147 self.cmd = self.cmd .. '&& touch ' .. w .. ' '
148 return self
149 end
150 function exe:Do()
151 --[[ "The condition expression of a control structure can return any
152 value. Both false and nil are considered false. All values different
153 from nil and false are considered true (in particular, the number 0
154 and the empty string are also true)."
155 says the docs, I beg to differ.]]
156 if true == self.log then D(" executing - &nbsp; <code>" .. self.cmd .. "</code>") end
157 --[[ Damn os.execute()
158 Lua 5.1 says it returns "a status code, which is system-dependent"
159 Lua 5.2 says it returns true/nil, "exit"/"signal", the status code.
160 I'm getting 7168 or 0. No idea what the fuck that is.
161 local ok, rslt, status = os.execute(s)
162 ]]
163 self.lines = APT.readCmd(self.cmd .. '; echo "$?"', 'r')
164 -- The last line will be the command's returned status, collect everything else in result.
165 self.status = tonumber(self.lines[#self.lines])
166 self.lines[#self.lines] = nil
167-- self.result = '\n'
168-- for i,l in ipairs(self.lines) do
169-- self.result = self.result .. l .. "\n"
170-- end
171 if (137 == self.status) or (124 == self.status) then
172 print("timeout killed " .. self.status .. ' ' .. self.command)
173 print('ERROR ' .. "timeout killed " .. self.status .. ' ' .. self.command)
174 elseif (nil == self.status) then
175 print("STATUS |" .. "NIL" .. '| ' .. self.command)
176 elseif (0 ~= self.status) then
177 print("STATUS |" .. self.status .. '| ' .. self.command)
178 end
179 return self
180 end
181 function exe:fork(host)
182 if nil ~= host then self.cmd = self.cmd .. '; r=$?; if [ $r -ge 124 ]; then echo "$r ' .. host .. ' failed forked command ' .. string.gsub(self.cmd, '"', "'") .. '"; fi' end
183 self.cmd = '{ ' .. self.cmd .. '; } &'
184 if true == self.log then D(" forking - &nbsp; <code>" .. self.cmd .. "</code>") end
185 os.execute(self.cmd)
186 return self
187 end
188 return exe
189end
190
191APT.exists = function(c)
192 if 0 == APT.exe('which ' .. c):Do().status then return true end
193 return false
194end
195
196APT.killEmAll = function(all)
197 for i,l in ipairs(all) do
198 local c = 0
199 while 0 ~= tonumber(APT.exe("pgrep -u $USER -xc " .. l):Do().lines[1]) do
200 local s = 'TERM'
201 if c > 1 then s = 'KILL'; APT.exe("sleep " .. c):Do() end
202 print( "pkill -" .. s .. " -u $USER -x " .. l)
203 APT.exe("pkill -" .. s .. " -u $USER -x " .. l):Do()
204 c = c + 1
205 end
206 end
207end
208
209
210
211local args = {...} 95local args = {...}
212if 0 ~= #args then 96if 0 ~= #args then
213-- for i,a in pairs(args) do 97-- for i,a in pairs(args) do
@@ -216,23 +100,27 @@ if 0 ~= #args then
216 100
217 if 'start' == args[1] then 101 if 'start' == args[1] then
218 elseif 'stop' == args[1] then 102 elseif 'stop' == args[1] then
219 APT.killEmAll{'qsynth'} 103 _.killEmAll{'qsynth'}
220 APT.exe("a2j_control --stop"):Do() 104 __[[
221 APT.exe("sleep 2"):Do() 105 a2j_control --stop
222 APT.exe("a2j_control --exit"):Do() 106 sleep 2
223 APT.exe("sleep 2"):Do() 107 a2j_control --exit
224 APT.killEmAll{'alsa_in', 'alsa_out', 'zita-a2j', 'zita-j2a', 'aseqjoy', 'jack-plumbing'} 108 sleep 2
225 APT.exe("sleep 2"):Do() 109]]:Do()
226 APT.exe("jack_control stop"):Do() 110 _.killEmAll{'alsa_in', 'alsa_out', 'zita-a2j', 'zita-j2a', 'aseqjoy', 'jack-plumbing'}
227 APT.exe("sleep 2"):Do() 111 __[[
228 APT.exe("jack_control exit"):Do() 112 sleep 2
229 APT.exe("sleep 2"):Do() 113 jack_control stop
230 APT.killEmAll{'jmcore', 'qjackctl'} 114 sleep 2
115 jack_control exit
116 sleep 2
117]]:Do()
118 _.killEmAll{'jmcore', 'qjackctl'}
231 -- Catia is python, and no easy way to kill it. 119 -- Catia is python, and no easy way to kill it.
232 -- Also it keeps jackdbus alive, no matter how hard you kill it. 120 -- Also it keeps jackdbus alive, no matter how hard you kill it.
233 APT.exe("pkill -TERM -u $USER -f catia"):Do() 121 __("pkill -TERM -u $USER -f catia"):Do()
234 APT.exe("sleep 2"):Do() 122 __("sleep 2"):Do()
235 APT.killEmAll{'jackdbus', 'a2jmidid'} 123 _.killEmAll{'jackdbus', 'a2jmidid'}
236 return(0) 124 return(0)
237 elseif 'JACK' == args[1] then 125 elseif 'JACK' == args[1] then
238 elseif 'restart' == args[1] then args[1] = 'start' 126 elseif 'restart' == args[1] then args[1] = 'start'
@@ -261,31 +149,31 @@ end
261local asoundrcPath = '/var/lib/aataaj' 149local asoundrcPath = '/var/lib/aataaj'
262local asoundrc = 'asoundrc' 150local asoundrc = 'asoundrc'
263local GUI = 'qjackctl' 151local GUI = 'qjackctl'
264if APT.exists('catia') then GUI = 'catia' end 152if _.exists('catia') then GUI = 'catia' end
265local alias = { 153local alias = {
266-- {name='Screen', dev='HDMI9'}, 154-- {name='Screen', dev='HDMI9'},
267 } 155 }
268 156
269local speaker = 'espeak' 157local speaker = 'espeak'
270if APT.exists('espeak-ng') then speaker = 'espeak-ng' end 158if _.exists('espeak-ng') then speaker = 'espeak-ng' end
271 159
272local Cards = {} 160local Cards = {}
273 161
274print('Scanning for audio devices.') 162print('Scanning for audio devices.')
275local cards = APT.exe('ls -d1 /proc/asound/card[0-9]*'):noErr():Do() 163local cards = __('ls -d1 /proc/asound/card[0-9]*'):noErr():Do()
276for i,l in ipairs(cards.lines) do 164for i,l in ipairs(cards.lines) do
277 local f, e = io.open(l .. '/id', "r") 165 local f, e = io.open(l .. '/id', "r")
278 if nil == f then print("Could not open " .. l .. '/id') else 166 if nil == f then print("Could not open " .. l .. '/id') else
279 Cards[l] = {path = l, name = f:read("*a"):sub(1, -2), devs = {}, captureDevs = {}, playbackDevs = {}} 167 Cards[l] = {path = l, name = f:read("*a"):sub(1, -2), devs = {}, captureDevs = {}, playbackDevs = {}}
280 if "Loopback" ~= Cards[l]['name'] then 168 if "Loopback" ~= Cards[l]['name'] then
281 Cards[l]['capture'] = APT.exe('ls -d1 ' .. l .. '/pcm[0-9]*c*'):noErr():Do() 169 Cards[l]['capture'] = __('ls -d1 ' .. l .. '/pcm[0-9]*c*'):noErr():Do()
282 for j,c in ipairs(Cards[l]['capture'].lines) do 170 for j,c in ipairs(Cards[l]['capture'].lines) do
283 local n = c:match(".*pcm(%d+).*") 171 local n = c:match(".*pcm(%d+).*")
284 Cards[l]['captureDevs'][j] = n 172 Cards[l]['captureDevs'][j] = n
285 Cards[l]['devs'][n] = n 173 Cards[l]['devs'][n] = n
286 print("\tFound capture device: " .. Cards[l]['name'] .. "\tDEVICE: " .. Cards[l]['captureDevs'][j] .. ' ' .. n) 174 print("\tFound capture device: " .. Cards[l]['name'] .. "\tDEVICE: " .. Cards[l]['captureDevs'][j] .. ' ' .. n)
287 end 175 end
288 Cards[l]['playback'] = APT.exe('ls -d1 ' .. l .. '/pcm[0-9]*p*'):noErr():Do() 176 Cards[l]['playback'] = __('ls -d1 ' .. l .. '/pcm[0-9]*p*'):noErr():Do()
289 for j,p in ipairs(Cards[l]['playback'].lines) do 177 for j,p in ipairs(Cards[l]['playback'].lines) do
290 local n = p:match(".*pcm(%d+).*") 178 local n = p:match(".*pcm(%d+).*")
291 Cards[l]['playbackDevs'][j] = n 179 Cards[l]['playbackDevs'][j] = n
@@ -293,8 +181,8 @@ for i,l in ipairs(cards.lines) do
293 print("\tFound playback device " .. i - 1 .. " : " .. Cards[l]['name'] .. "\tDEVICE: " .. Cards[l]['playbackDevs'][j] .. ' ' .. n) 181 print("\tFound playback device " .. i - 1 .. " : " .. Cards[l]['name'] .. "\tDEVICE: " .. Cards[l]['playbackDevs'][j] .. ' ' .. n)
294 if 'JACK' ~= args[1] then 182 if 'JACK' ~= args[1] then
295 print('\t\tALSA_CARD=' .. i - 1 .. ' ' .. speaker .. ' "Found playback device ' .. i - 1 .. ' : ' .. Cards[l]['name'] .. ' DEVICE: ' .. Cards[l]['playbackDevs'][j] .. ' ' .. n .. '"') 183 print('\t\tALSA_CARD=' .. i - 1 .. ' ' .. speaker .. ' "Found playback device ' .. i - 1 .. ' : ' .. Cards[l]['name'] .. ' DEVICE: ' .. Cards[l]['playbackDevs'][j] .. ' ' .. n .. '"')
296 APT.exe('ALSA_CARD=' .. i - 1 .. ' ' .. speaker .. ' "Found playback device ' .. i - 1 .. ' : ' .. Cards[l]['name'] .. ' DEVICE: ' .. Cards[l]['playbackDevs'][j] .. ' ' .. n .. '"'):noErr():Do() 184 __('ALSA_CARD=' .. i - 1 .. ' ' .. speaker .. ' "Found playback device ' .. i - 1 .. ' : ' .. Cards[l]['name'] .. ' DEVICE: ' .. Cards[l]['playbackDevs'][j] .. ' ' .. n .. '"'):noErr():Do()
297 APT.exe('sleep 1') 185 __('sleep 1'):Do()
298 end 186 end
299 end 187 end
300 end 188 end
@@ -302,7 +190,7 @@ for i,l in ipairs(cards.lines) do
302end 190end
303 191
304if 'start' == args[1] then 192if 'start' == args[1] then
305 APT.exe('mkdir -p ' .. asoundrcPath):Do() 193 __('mkdir -p ' .. asoundrcPath):Do()
306 local a, e = io.open(asoundrcPath .. '/jack-plumbing', "w") 194 local a, e = io.open(asoundrcPath .. '/jack-plumbing', "w")
307 if nil == a then print("Could not open " .. asoundrcPath .. '/jack-plumbing') else 195 if nil == a then print("Could not open " .. asoundrcPath .. '/jack-plumbing') else
308 a:write([[ 196 a:write([[
@@ -475,58 +363,58 @@ elseif 'JACK' == args[1] then
475 print('') 363 print('')
476 print("Start up JACK and friends.") 364 print("Start up JACK and friends.")
477 print("jack_control") 365 print("jack_control")
478 APT.exe('jack_control start'):Do() 366 __('jack_control start'):Do()
479 APT.exe('jack_control ds alsa'):Do() 367 __('jack_control ds alsa'):Do()
480 --jack_control dps device hw:RIG,0 368 --jack_control dps device hw:RIG,0
481 local r = APT.exe('jack_control status'):Do().status 369 local r = __('jack_control status'):Do().status
482 while r ~= 0 do 370 while r ~= 0 do
483 print("Waiting for JACK - sleep 1") 371 print("Waiting for JACK - sleep 1")
484 APT.exe('sleep 1'):Do() 372 __('sleep 1'):Do()
485 r = APT.exe('jack_control status'):Do().status 373 r = __('jack_control status'):Do().status
486 end 374 end
487 if nil ~= GUI then 375 if nil ~= GUI then
488 print(GUI) 376 print(GUI)
489 APT.exe(GUI):fork() 377 __(GUI):fork()
490 end 378 end
491 if APT.exists('jack-plumbing') then 379 if _.exists('jack-plumbing') then
492 print("jack-plumbing") 380 print("jack-plumbing")
493 APT.exe('jack-plumbing -o /var/lib/aataaj/jack-plumbing 2>/dev/null'):fork() 381 __('jack-plumbing -o /var/lib/aataaj/jack-plumbing 2>/dev/null'):fork()
494 end 382 end
495 if APT.exists('a2j_control') then 383 if _.exists('a2j_control') then
496 -- Bridge ALSA ports to JACK ports. Only handles MIDI. 384 -- Bridge ALSA ports to JACK ports. Only handles MIDI.
497 -- MIDI via a2jmidid. The --ehw enables hardware ports as well, equal to using the seq MIDI drivare according to https://freeshell.de/~murks/posts/ALSA_and_JACK_MIDI_explained_(by_a_dummy_for_dummies)/ 385 -- MIDI via a2jmidid. The --ehw enables hardware ports as well, equal to using the seq MIDI drivare according to https://freeshell.de/~murks/posts/ALSA_and_JACK_MIDI_explained_(by_a_dummy_for_dummies)/
498 --a2j_control actually starts a2jmidid. 386 --a2j_control actually starts a2jmidid.
499 ----a2jmidid -e -u & 387 ----a2jmidid -e -u &
500 -- I think the jack_control start and my current alsa config means a2jmidid gets started anyway. But seem to need this bit to get the joystick covered. 388 -- I think the jack_control start and my current alsa config means a2jmidid gets started anyway. But seem to need this bit to get the joystick covered.
501 print("a2j_control") 389 print("a2j_control")
502 APT.exe('a2j_control --ehw && a2j_control --start'):Do() 390 __('a2j_control --ehw && a2j_control --start'):Do()
503-- print("sleep 2") 391-- print("sleep 2")
504-- APT.exe('sleep 2'):Do() 392-- __('sleep 2'):Do()
505 print("") 393 print("")
506 end 394 end
507 395
508 396
509 local AIN = "alsa_in" 397 local AIN = "alsa_in"
510 if APT.exists('zita-a2j') then AIN = 'zita-a2j' end 398 if _.exists('zita-a2j') then AIN = 'zita-a2j' end
511 local AOUT = "alsa_out" 399 local AOUT = "alsa_out"
512 if APT.exists('zita-j2a') then AOUT = 'zita-j2a' end 400 if _.exists('zita-j2a') then AOUT = 'zita-j2a' end
513 401
514 print("Basic ALSA sound devices converted to JACK.") 402 print("Basic ALSA sound devices converted to JACK.")
515 for i,C in pairs(alias) do 403 for i,C in pairs(alias) do
516 print('HW playback: ' .. C['name'] .. '\tDEVICE: ' .. C['dev']) 404 print('HW playback: ' .. C['name'] .. '\tDEVICE: ' .. C['dev'])
517 APT.exe(AOUT .. ' -j ' .. C['name'] .. ' -d ' .. C['dev']):fork() 405 __(AOUT .. ' -j ' .. C['name'] .. ' -d ' .. C['dev']):fork()
518 end 406 end
519 print("HW playback: cloop\tDEVICE: cloop") 407 print("HW playback: cloop\tDEVICE: cloop")
520 -- No idea why, cloop wont work with zita-a2j. 408 -- No idea why, cloop wont work with zita-a2j.
521 APT.exe('alsa_in -j cloop -d cloop'):fork() 409 __('alsa_in -j cloop -d cloop'):fork()
522 --APT.exe('sleep 1'):Do() 410 --__('sleep 1'):Do()
523 --APT.exe('jack_connect cloop:capture_1 system:playback_1'):Do() 411 --__('jack_connect cloop:capture_1 system:playback_1'):Do()
524 --APT.exe('jack_connect cloop:capture_2 system:playback_2'):Do() 412 --__('jack_connect cloop:capture_2 system:playback_2'):Do()
525 print("HW playback: ploop\tDEVICE: ploop") 413 print("HW playback: ploop\tDEVICE: ploop")
526 APT.exe('alsa_out -j ploop -d ploop'):fork() 414 __('alsa_out -j ploop -d ploop'):fork()
527 --APT.exe('sleep 1'):Do() 415 --__('sleep 1'):Do()
528 --APT.exe('jack_connect system:capture_1 ploop:playback_1'):Do() 416 --__('jack_connect system:capture_1 ploop:playback_1'):Do()
529 --APT.exe('jack_connect system:capture_2 ploop:playback_2'):Do() 417 --__('jack_connect system:capture_2 ploop:playback_2'):Do()
530 418
531 419
532 print("") 420 print("")
@@ -535,39 +423,39 @@ elseif 'JACK' == args[1] then
535 for i,C in pairs(Cards) do 423 for i,C in pairs(Cards) do
536 for j,c in ipairs(C['playbackDevs']) do 424 for j,c in ipairs(C['playbackDevs']) do
537 print("HW playback: " .. C['name'] .. "\tDEVICE: " .. C['playbackDevs'][j]) 425 print("HW playback: " .. C['name'] .. "\tDEVICE: " .. C['playbackDevs'][j])
538 APT.exe(AOUT .. ' -j ' .. C['name'] .. "_" .. C['playbackDevs'][j] .. '-in -d ' .. C['name'] .. C['playbackDevs'][j]):fork() 426 __(AOUT .. ' -j ' .. C['name'] .. "_" .. C['playbackDevs'][j] .. '-in -d ' .. C['name'] .. C['playbackDevs'][j]):fork()
539 -- APT.exe('sleep 1'):Do() 427 -- __('sleep 1'):Do()
540 -- APT.exe('jack_connect cloop:capture_1 ' .. C['name'] .. '_' .. C['playbackDevs'][j] .. '-in' .. ':playback_1'):Do() 428 -- __('jack_connect cloop:capture_1 ' .. C['name'] .. '_' .. C['playbackDevs'][j] .. '-in' .. ':playback_1'):Do()
541 -- APT.exe('jack_connect cloop:capture_2 ' .. C['name'] .. '_' .. C['playbackDevs'][j] .. '-in' .. ':playback_2'):Do() 429 -- __('jack_connect cloop:capture_2 ' .. C['name'] .. '_' .. C['playbackDevs'][j] .. '-in' .. ':playback_2'):Do()
542 end 430 end
543 for j,c in ipairs(C['captureDevs']) do 431 for j,c in ipairs(C['captureDevs']) do
544 print("HW capture: " .. C['name'] .. "\tDEVICE: " .. C['captureDevs'][j]) 432 print("HW capture: " .. C['name'] .. "\tDEVICE: " .. C['captureDevs'][j])
545 APT.exe(AIN .. ' -j ' .. C['name'] .. "_" .. C['captureDevs'][j] .. '-out -d ' .. C['name'] .. C['captureDevs'][j]):fork() 433 __(AIN .. ' -j ' .. C['name'] .. "_" .. C['captureDevs'][j] .. '-out -d ' .. C['name'] .. C['captureDevs'][j]):fork()
546 end 434 end
547 end 435 end
548 print("") 436 print("")
549 437
550 if APT.exists('aseqjoy') then 438 if _.exists('aseqjoy') then
551 print("Scanning for joysticks.") 439 print("Scanning for joysticks.")
552 local sticks = APT.exe('ls -1 /dev/input/js[0-9]*'):noErr():Do() 440 local sticks = __('ls -1 /dev/input/js[0-9]*'):noErr():Do()
553 for i,l in ipairs(sticks.lines) do 441 for i,l in ipairs(sticks.lines) do
554 print("aseqjoy " .. l) 442 print("aseqjoy " .. l)
555 -- Buttons switch to that numbered MIDI channel, defaults to 1. 443 -- Buttons switch to that numbered MIDI channel, defaults to 1.
556 -- Axis are mapped to MIDI controllers 10 - 15 444 -- Axis are mapped to MIDI controllers 10 - 15
557 -- -r means to use high resolution MIDI values. 445 -- -r means to use high resolution MIDI values.
558 APT.exe('aseqjoy -d ' .. l:sub(-1,-1) .. ' -r'):fork() 446 __('aseqjoy -d ' .. l:sub(-1,-1) .. ' -r'):fork()
559 end 447 end
560 print("") 448 print("")
561 end 449 end
562 450
563 if APT.exists('jack-plumbing') then 451 if _.exists('jack-plumbing') then
564 print('Stop our jack-plumbing, eventually.') 452 print('Stop our jack-plumbing, eventually.')
565 APT.exe('sleep 4'):Do() 453 __('sleep 4'):Do()
566 APT.killEmAll{"jack-plumbing"} 454 _.killEmAll{"jack-plumbing"}
567 end 455 end
568 456
569 if APT.exists('~/.aataaj_JACK.lua') then 457 if _.exists('~/.aataaj_JACK.lua') then
570 print('Running users aataaj_JACK.lua') 458 print('Running users aataaj_JACK.lua')
571 APT.exe('~/.aataaj_JACK.lua'):Do() 459 __('~/.aataaj_JACK.lua'):Do()
572 end 460 end
573end 461end