Advertisement
Muzze77

Play N STop23

Apr 4th, 2014
141
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.48 KB | None | 0 0
  1. -- Reusable code is mostly inside this do-block
  2. do-- Reusable code is mostly inside this do-block
  3. do
  4. local baseWords, javaWords, immedWords = {}, {}, {}
  5.  
  6. speaker = {}
  7.  
  8. baseWords.WORDS = 1
  9. baseWords.PAGE = 2
  10. baseWords["."] = 3
  11. baseWords["(lit)"] = 4
  12. baseWords["(lit2)"] = 5
  13. baseWords.RETURN = 6
  14. baseWords.CR = 7
  15. baseWords.DUP = 8
  16. baseWords["?DUP"] = 9
  17. baseWords["2DUP"] = 10
  18. baseWords.DROP = 11
  19. baseWords["2DROP"] = 12
  20. baseWords.SWAP = 13
  21. baseWords["2SWAP"] = 14
  22. baseWords.OVER = 16
  23. baseWords["2OVER"] = 17
  24. baseWords.ROT = 18
  25. baseWords["-ROT"] = 19
  26. baseWords.NIP = 20
  27. baseWords.TUCK = 21
  28. baseWords.DO = 22
  29. baseWords.LOOP = 24
  30. baseWords["+LOOP"] = 25
  31. function immedWords.BEGIN(t)
  32. t.push(t.here())
  33. end
  34. function immedWords.UNTIL(t)
  35. t.compileWord("(!?branch)")
  36. table.insert(t.code, t.pop())
  37. end
  38. function immedWords.WHILE(t)
  39. local BEGIN = t.pop()
  40. t.compileWord("(!?branch)")
  41. local WHILE = t.here()
  42. table.insert(t.code, 0)
  43. t.push(WHILE)
  44. t.push(BEGIN)
  45. end
  46. function immedWords.REPEAT(t)
  47. t.compileWord("(branch)")
  48. table.insert(t.code, t.pop())
  49. t.code[t.pop()] = t.here()
  50. end
  51. function immedWords.AGAIN(t)
  52. t.compileWord("(branch)")
  53. table.insert(t.code, t.pop())
  54. end
  55. baseWords.UNLOOP = 32
  56. function immedWords.IF(t)
  57. t.compileWord("(!?branch)")
  58. t.push(t.here())
  59. table.insert(t.code, 0)
  60. end
  61. function immedWords.THEN(t)
  62. t.code[t.pop()] = t.here()
  63. end
  64. function immedWords.ELSE(t)
  65. local IF = t.pop()
  66. t.compileWord("(branch)")
  67. t.push(t.here())
  68. table.insert(t.code, 0)
  69. t.code[IF] = t.here()
  70. end
  71. baseWords["0="] = 36
  72. baseWords["0<>"] = 37
  73. baseWords["0<"] = 38
  74. baseWords["0>"] = 39
  75. baseWords["<>"] = 40
  76. baseWords[">"] = 41
  77. baseWords["<"] = 42
  78. baseWords[">="] = 43
  79. baseWords["<="] = 44
  80. baseWords["="] = 45
  81. baseWords["+"] = 46
  82. baseWords["-"] = 47
  83. baseWords["/"] = 48
  84. baseWords["MOD"] = 49
  85. baseWords["1+"] = 50
  86. baseWords["1-"] = 51
  87. baseWords.NEGATE = 52
  88. baseWords.MAX = 53
  89. baseWords.MIN = 54
  90. baseWords.AND = 55
  91. baseWords.OR = 56
  92. baseWords.XOR = 57
  93. baseWords.INVERT = 58
  94. baseWords.TRUE = 59
  95. baseWords.FALSE = 60
  96. baseWords["2*"] = 61
  97. baseWords["2/"] = 62
  98. baseWords.I = 63
  99. baseWords.J = 64
  100. baseWords["*"] = 67
  101. baseWords["(branch)"] = 68
  102. baseWords["(?branch)"] = 69
  103. baseWords["(!?branch)"] = 70
  104. function immedWords.VARIABLE(t)
  105. t.forthWords[t.nextWord()] = t.here()
  106. t.compileWord("(lit)")
  107. table.insert(t.code, t.here() + 2)
  108. t.compileWord("RETURN")
  109. end
  110. baseWords["@"] = 72
  111. baseWords["!"] = 73
  112. baseWords.TICK = 74
  113.  
  114. javaWords.PLAY = 1
  115. javaWords.STOP = 2
  116.  
  117. function speaker.compileForth(sourcecode)
  118. local code = {}
  119. local forthWords = {}
  120. local input = {}
  121.  
  122. local definingWord = nil
  123.  
  124. local compileStack = {}
  125.  
  126. local immedContext = {
  127. code = code,
  128. here = function()
  129. return #code
  130. end,
  131. push = function(v)
  132. -- if #compileStack > 128 then error("Compile stack overflow", 0) end
  133. table.insert(compileStack, v)
  134. end,
  135. pop = function()
  136. return table.remove(compileStack, #compileStack) or error("Compile stack empty", 0)
  137. end,
  138. forthWords = forthWords,
  139. }
  140.  
  141. local function compileWord(word)
  142. if baseWords[word] then
  143. table.insert(code, 0xC000 + baseWords[word])
  144. elseif javaWords[word] then
  145. table.insert(code, 0x8000 + javaWords[word])
  146. elseif forthWords[word] then
  147. table.insert(code, forthWords[word])
  148. elseif immedWords[word] then
  149. immedWords[word](immedContext)
  150. elseif tonumber(word) then
  151. local n = math.floor(tonumber(word))
  152. if n < -2^31 or n >= 2^31 then
  153. error("number out of range: "..n)
  154. end
  155. if n >= -32768 and n < 32768 then
  156. if n < 0 then n = n + 65536 end
  157.  
  158. compileWord("(lit)")
  159. table.insert(code, n)
  160. else
  161. if n < 0 then n = n + 2^32 end
  162.  
  163. compileWord("(lit2)")
  164. table.insert(code, n % 65536)
  165. table.insert(code, math.floor(n / 65536))
  166. end
  167. else
  168. error("unknown word: "..word)
  169. end
  170. end
  171.  
  172. immedContext.compileWord = compileWord
  173.  
  174. for word in string.gmatch(sourcecode, "[^ ]+") do
  175. table.insert(input, word)
  176. end
  177.  
  178. compileWord("(branch)")
  179. table.insert(code, 0)
  180.  
  181. local pos = 1
  182.  
  183. function immedContext.nextWord()
  184. pos = pos + 1
  185. return input[pos - 1] or "EOF"
  186. end
  187. while pos <= #input do
  188. if input[pos] == ":" then
  189. if definingWord then
  190. error(": inside word definition")
  191. end
  192.  
  193. local name = input[pos + 1] or "EOF"
  194. definingWord = {name, #code}
  195. pos = pos + 2
  196. elseif input[pos] == ";" then
  197. if definingWord == nil then
  198. error("; outside of word definition")
  199. end
  200. compileWord("RETURN")
  201.  
  202. forthWords[definingWord[1]] = definingWord[2]
  203.  
  204. definingWord = nil
  205. pos = pos + 1
  206. elseif input[pos] == "RECURSE" then
  207. if not definingWord then
  208. error("RECURSE outside of word definition")
  209. end
  210. table.insert(code, definingWord[2])
  211. pos = pos + 1
  212. else
  213. compileWord(input[pos])
  214. pos = pos + 1
  215. end
  216. end
  217.  
  218. if definingWord then
  219. error("unfinished word definition")
  220. end
  221.  
  222. if forthWords.MAIN then
  223. code[2] = forthWords.MAIN
  224. else
  225. error("No MAIN word")
  226. end
  227.  
  228.  
  229. return code
  230. end
  231.  
  232. function speaker.uploadForth(code, side, hot)
  233. assert(peripheral.getType(side) == "speaker", "No speaker on "..side.." side")
  234. if not hot then peripheral.call(side, "shutdown") end
  235. local pos = 1
  236. local addr = 0
  237. while pos <= #code do
  238. local _end = math.min(#code, pos + 100)
  239. repeat
  240. local err = peripheral.call(side, "write", addr, unpack(code, pos, _end))
  241. if err == "Buffer full" then
  242. sleep(0.05)
  243. elseif err then
  244. error(err, 0)
  245. end
  246. until err == nil
  247. addr = addr + _end + 1 - pos
  248. pos = _end + 1
  249. end
  250. if not hot then peripheral.call(side, "reboot")
  251. else peripheral.call(side, "execute", 0)
  252. end
  253. end
  254.  
  255. function speaker.streamTuneUsingForth(tune, side)
  256. local ticks = 0
  257. local size = 0 -- approximate shorts used
  258.  
  259. local fcode
  260.  
  261. peripheral.call(side, "shutdown")
  262.  
  263. local function play()
  264. local cf = speaker.compileForth(fcode .. " ;")
  265. speaker.uploadForth(cf, side, true)
  266. sleep(math.max(1, ticks) * 0.05)
  267. end
  268. local function start()
  269. fcode = ": TICKS 0 DO TICK LOOP ; : MAIN ";
  270. size = 0
  271. ticks = 0
  272. end
  273.  
  274. start()
  275. for _,v in ipairs(tune) do
  276. if v[1] == "p" then
  277. fcode = fcode .. v[3] .. " " .. v[2] .. " PLAY "
  278. size = size + 5
  279. elseif v[1] == "s" then
  280. fcode = fcode .. v[2] .. " STOP "
  281. size = size + 3
  282. elseif v[1] == "w" then
  283. fcode = fcode .. v[2] .. " TICKS "
  284. size = size + 3
  285. ticks = ticks + v[2]
  286. end
  287.  
  288. if size > 10000 then
  289. play()
  290. start()
  291. end
  292. end
  293.  
  294. if size > 0 then
  295. play()
  296. end
  297.  
  298. peripheral.call(side, "shutdown")
  299. end
  300.  
  301. function speaker.streamTuneDirectly(tune, side)
  302. peripheral.call(side, "shutdown")
  303. for _,v in ipairs(tune) do
  304. if v[1] == "p" then
  305. peripheral.call(side, "start", v[2], v[3])
  306. elseif v[1] == "s" then
  307. peripheral.call(side, "stop", v[2])
  308. elseif v[1] == "w" then
  309. sleep(v[2] * 0.05)
  310. end
  311. end
  312. peripheral.call(side, "shutdown")
  313. end
  314. end
  315.  
  316. local function words(s)
  317. local pos = 1
  318. local function f()
  319. if pos == 0 then return nil end
  320.  
  321. local start, next = pos, s:find(" ", pos)
  322. if not next then
  323. pos = 0
  324. if start > #s then
  325. return nil
  326. end
  327. return s:sub(start)
  328. end
  329.  
  330. pos = next + 1
  331. if start == next then
  332. return f()
  333. end
  334. return s:sub(start, next-1)
  335. end
  336. return f
  337. end
  338.  
  339. local function compileTune(src)
  340. local stack = {}
  341. local rv = {}
  342. local function pop() return table.remove(stack, #stack) end
  343. for word in words(src) do
  344. if tonumber(word) then
  345. stack[#stack+1] = tonumber(word)
  346. elseif word == "TICKS" then
  347. rv[#rv+1] = {"w", pop()} -- w, ticks
  348. elseif word == "PLAY" then
  349. rv[#rv+1] = {"p", pop()} -- p, ch, freq
  350. rv[#rv][3] = pop()
  351. elseif word == "STOP" then
  352. rv[#rv+1] = {"s", pop()}
  353. else
  354. error("unknown word: "..word,0)
  355. end
  356. end
  357. return rv
  358. end
  359.  
  360. term.clear()
  361. for k=1,#names do
  362. term.setCursorPos(1, k)
  363. term.write(" " .. names[k])
  364. end
  365.  
  366. local speakerSide
  367. function findSide()
  368. for _,side in ipairs(redstone.getSides()) do
  369. if peripheral.getType(side) == "speaker" then
  370. speakerSide = side
  371. break
  372. end
  373. end
  374.  
  375. if not speakerSide then
  376. error("No attached speakers!")
  377. end
  378. end
  379.  
  380. local ok, err = pcall(function()
  381. findSide() -- error early if no speakers attached
  382.  
  383. local sel = 1
  384. term.setCursorPos(2, sel)
  385. term.write("*")
  386. while true do
  387. local _, key = os.pullEvent("key")
  388. if key == 208 then
  389. -- down
  390. if sel < #names then
  391. term.setCursorPos(2, sel)
  392. term.write(" ")
  393. sel = sel + 1
  394. term.setCursorPos(2, sel)
  395. term.write("*")
  396. end
  397. elseif key == 200 then
  398. -- up
  399. if sel > 1 then
  400. term.setCursorPos(2, sel)
  401. term.write(" ")
  402. sel = sel - 1
  403. term.setCursorPos(2, sel)
  404. term.write("*")
  405. end
  406. elseif key == 28 then
  407. -- enter
  408. break
  409. end
  410. end
  411.  
  412. local w, h = term.getSize()
  413.  
  414. local function centre(y, str)
  415. term.setCursorPos(math.ceil((w - #str) / 2), y)
  416. term.clearLine(y)
  417. term.write(str)
  418. end
  419. local y = math.floor(h/2-1.5)
  420. term.clear()
  421. centre(y, names[sel])
  422. local useDirect = false
  423. centre(y+2, "[Forth streaming]")
  424. centre(y+3, "Direct streaming")
  425. while true do
  426. local _, key = os.pullEvent("key")
  427. if key == 200 then
  428. -- up
  429. useDirect = false
  430. centre(y+2, "[Forth streaming]")
  431. centre(y+3, "Direct streaming")
  432. elseif key == 208 then
  433. -- down
  434. useDirect = true
  435. centre(y+2, "Forth streaming")
  436. centre(y+3, "[Direct streaming]")
  437. elseif key == 28 then
  438. -- enter
  439. break
  440. end
  441. end
  442.  
  443. term.clearLine(y+2)
  444. term.clearLine(y+3)
  445. centre(y+2, useDirect and "Direct streaming" or "Forth streaming")
  446.  
  447. findSide()
  448.  
  449. local tune = compileTune(tunesrc[sel])
  450. peripheral.call(speakerSide, "setAttenuation", 20)
  451.  
  452. local func = useDirect and speaker.streamTuneDirectly or speaker.streamTuneUsingForth
  453. func(tune, speakerSide)
  454. end)
  455.  
  456. term.clear()
  457. term.setCursorPos(1, 1)
  458.  
  459. pcall(peripheral.call, speakerSide, "shutdown")
  460.  
  461. if not ok then error(err, 0) end
  462.  
  463. --[[peripheral.call("right", "debugOff")
  464. forth.sendToSpeaker(forth.compile(": MAIN 2 1 . . ;"), "right")
  465. sleep(0.5)
  466. peripheral.call("right", "debugOn")
  467. forth.sendToSpeaker(forth.compile(": MAIN 4 3 . . ;"), "right")
  468. sleep(0.5)]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement