Advertisement
arismoko

wave library

Sep 29th, 2021
30
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.84 KB | None | 0 0
  1. --[[
  2. wave version 0.1.5
  3.  
  4. The MIT License (MIT)
  5. Copyright (c) 2020 CrazedProgrammer
  6.  
  7. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
  8. associated documentation files (the "Software"), to deal in the Software without restriction,
  9. including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
  10. and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
  11. so, subject to the following conditions:
  12.  
  13. The above copyright notice and this permission notice shall be included in all copies or
  14. substantial portions of the Software.
  15.  
  16. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  17. INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  18. PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  19. COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
  20. AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. ]]
  23.  
  24. local wave = { }
  25. wave.version = "0.1.5"
  26.  
  27. wave._oldSoundMap = {"harp", "bassattack", "bd", "snare", "hat"}
  28. wave._newSoundMap = {"harp", "bass", "basedrum", "snare", "hat"}
  29. wave._defaultThrottle = 99
  30. wave._defaultClipMode = 1
  31. wave._maxInterval = 1
  32. wave._isNewSystem = false
  33. if _HOST then
  34. wave._isNewSystem = _HOST:sub(15, #_HOST) >= "1.80"
  35. end
  36.  
  37. wave.context = { }
  38. wave.output = { }
  39. wave.track = { }
  40. wave.instance = { }
  41.  
  42. function wave.createContext(clock, volume)
  43. clock = clock or os.clock()
  44. volume = volume or 1.0
  45.  
  46. local context = setmetatable({ }, {__index = wave.context})
  47. context.outputs = { }
  48. context.instances = { }
  49. context.vs = {0, 0, 0, 0, 0}
  50. context.prevClock = clock
  51. context.volume = volume
  52. return context
  53. end
  54.  
  55. function wave.context:addOutput(...)
  56. local output = wave.createOutput(...)
  57. self.outputs[#self.outputs + 1] = output
  58. return output
  59. end
  60.  
  61. function wave.context:addOutputs(...)
  62. local outs = {...}
  63. if #outs == 1 then
  64. if not getmetatable(outs) then
  65. outs = outs[1]
  66. else
  67. if getmetatable(outs).__index ~= wave.outputs then
  68. outs = outs[1]
  69. end
  70. end
  71. end
  72. for i = 1, #outs do
  73. self:addOutput(outs[i])
  74. end
  75. end
  76.  
  77. function wave.context:removeOutput(out)
  78. if type(out) == "number" then
  79. table.remove(self.outputs, out)
  80. return
  81. elseif type(out) == "table" then
  82. if getmetatable(out).__index == wave.output then
  83. for i = 1, #self.outputs do
  84. if out == self.outputs[i] then
  85. table.remove(self.outputs, i)
  86. return
  87. end
  88. end
  89. return
  90. end
  91. end
  92. for i = 1, #self.outputs do
  93. if out == self.outputs[i].native then
  94. table.remove(self.outputs, i)
  95. return
  96. end
  97. end
  98. end
  99.  
  100. function wave.context:addInstance(...)
  101. local instance = wave.createInstance(...)
  102. self.instances[#self.instances + 1] = instance
  103. return instance
  104. end
  105.  
  106. function wave.context:removeInstance(instance)
  107. if type(instance) == "number" then
  108. table.remove(self.instances, instance)
  109. else
  110. for i = 1, #self.instances do
  111. if self.instances == instance then
  112. table.remove(self.instances, i)
  113. return
  114. end
  115. end
  116. end
  117. end
  118.  
  119. function wave.context:playNote(note, pitch, volume)
  120. volume = volume or 1.0
  121.  
  122. self.vs[note] = self.vs[note] + volume
  123. for i = 1, #self.outputs do
  124. self.outputs[i]:playNote(note, pitch, volume * self.volume)
  125. end
  126. end
  127.  
  128. function wave.context:update(interval)
  129. local clock = os.clock()
  130. interval = interval or (clock - self.prevClock)
  131.  
  132. self.prevClock = clock
  133. if interval > wave._maxInterval then
  134. interval = wave._maxInterval
  135. end
  136. for i = 1, #self.outputs do
  137. self.outputs[i].notes = 0
  138. end
  139. for i = 1, 5 do
  140. self.vs[i] = 0
  141. end
  142. if interval > 0 then
  143. for i = 1, #self.instances do
  144. local notes = self.instances[i]:update(interval)
  145. for j = 1, #notes / 3 do
  146. self:playNote(notes[j * 3 - 2], notes[j * 3 - 1], notes[j * 3])
  147. end
  148. end
  149. end
  150. end
  151.  
  152.  
  153.  
  154. function wave.createOutput(out, volume, filter, throttle, clipMode)
  155. volume = volume or 1.0
  156. filter = filter or {true, true, true, true, true}
  157. throttle = throttle or wave._defaultThrottle
  158. clipMode = clipMode or wave._defaultClipMode
  159.  
  160. local output = setmetatable({ }, {__index = wave.output})
  161. output.native = out
  162. output.volume = volume
  163. output.filter = filter
  164. output.notes = 0
  165. output.throttle = throttle
  166. output.clipMode = clipMode
  167. if type(out) == "function" then
  168. output.nativePlayNote = out
  169. output.type = "custom"
  170. return output
  171. elseif type(out) == "string" then
  172. if peripheral.getType(out) == "iron_noteblock" then
  173. if wave._isNewSystem then
  174. local nb = peripheral.wrap(out)
  175. output.type = "iron_noteblock"
  176. function output.nativePlayNote(note, pitch, volume)
  177. if output.volume * volume > 0 then
  178. nb.playSound("minecraft:block.note."..wave._newSoundMap[note], volume, math.pow(2, (pitch - 12) / 12))
  179. end
  180. end
  181. return output
  182. end
  183. elseif peripheral.getType(out) == "speaker" then
  184. if wave._isNewSystem then
  185. local nb = peripheral.wrap(out)
  186. output.type = "speaker"
  187. function output.nativePlayNote(note, pitch, volume)
  188. if output.volume * volume > 0 then
  189. nb.playNote(wave._newSoundMap[note], volume, pitch)
  190. end
  191. end
  192. return output
  193. end
  194. end
  195. elseif type(out) == "table" then
  196. if out.execAsync then
  197. output.type = "commands"
  198. if wave._isNewSystem then
  199. function output.nativePlayNote(note, pitch, volume)
  200. out.execAsync("playsound minecraft:block.note."..wave._newSoundMap[note].." record @a ~ ~ ~ "..tostring(volume).." "..tostring(math.pow(2, (pitch - 12) / 12)))
  201. end
  202. else
  203. function output.nativePlayNote(note, pitch, volume)
  204. out.execAsync("playsound note."..wave._oldSoundMap[note].." @a ~ ~ ~ "..tostring(volume).." "..tostring(math.pow(2, (pitch - 12) / 12)))
  205. end
  206. end
  207. return output
  208. elseif getmetatable(out) then
  209. if getmetatable(out).__index == wave.output then
  210. return out
  211. end
  212. end
  213. end
  214. end
  215.  
  216. function wave.scanOutputs()
  217. local outs = { }
  218. if commands then
  219. outs[#outs + 1] = wave.createOutput(commands)
  220. end
  221. local sides = peripheral.getNames()
  222. for i = 1, #sides do
  223. if peripheral.getType(sides[i]) == "iron_noteblock" then
  224. outs[#outs + 1] = wave.createOutput(sides[i])
  225. elseif peripheral.getType(sides[i]) == "speaker" then
  226. outs[#outs + 1] = wave.createOutput(sides[i])
  227. end
  228. end
  229. return outs
  230. end
  231.  
  232. function wave.output:playNote(note, pitch, volume)
  233. volume = volume or 1.0
  234.  
  235. if self.clipMode == 1 then
  236. if pitch < 0 then
  237. pitch = 0
  238. elseif pitch > 24 then
  239. pitch = 24
  240. end
  241. elseif self.clipMode == 2 then
  242. if pitch < 0 then
  243. while pitch < 0 do
  244. pitch = pitch + 12
  245. end
  246. elseif pitch > 24 then
  247. while pitch > 24 do
  248. pitch = pitch - 12
  249. end
  250. end
  251. end
  252. if self.filter[note] and self.notes < self.throttle then
  253. self.nativePlayNote(note, pitch, volume * self.volume)
  254. self.notes = self.notes + 1
  255. end
  256. end
  257.  
  258.  
  259.  
  260. function wave.loadTrack(path)
  261. local track = setmetatable({ }, {__index = wave.track})
  262. local handle = fs.open(path, "rb")
  263. if not handle then return end
  264.  
  265. local function readInt(size)
  266. local num = 0
  267. for i = 0, size - 1 do
  268. local byte = handle.read()
  269. if not byte then -- dont leave open file handles no matter what
  270. handle.close()
  271. return
  272. end
  273. num = num + byte * (256 ^ i)
  274. end
  275. return num
  276. end
  277. local function readStr()
  278. local length = readInt(4)
  279. if not length then return end
  280. local data = { }
  281. for i = 1, length do
  282. data[i] = string.char(handle.read())
  283. end
  284. return table.concat(data)
  285. end
  286.  
  287. -- Part #1: Metadata
  288. track.length = readInt(2) -- song length (ticks)
  289. track.height = readInt(2) -- song height
  290. track.name = readStr() -- song name
  291. track.author = readStr() -- song author
  292. track.originalAuthor = readStr() -- original song author
  293. track.description = readStr() -- song description
  294. track.tempo = readInt(2) / 100 -- tempo (ticks per second)
  295. track.autoSaving = readInt(1) == 0 and true or false -- auto-saving
  296. track.autoSavingDuration = readInt(1) -- auto-saving duration
  297. track.timeSignature = readInt(1) -- time signature (3 = 3/4)
  298. track.minutesSpent = readInt(4) -- minutes spent
  299. track.leftClicks = readInt(4) -- left clicks
  300. track.rightClicks = readInt(4) -- right clicks
  301. track.blocksAdded = readInt(4) -- blocks added
  302. track.blocksRemoved = readInt(4) -- blocks removed
  303. track.schematicFileName = readStr() -- midi/schematic file name
  304.  
  305. -- Part #2: Notes
  306. track.layers = { }
  307. for i = 1, track.height do
  308. track.layers[i] = {name = "Layer "..i, volume = 1.0}
  309. track.layers[i].notes = { }
  310. end
  311.  
  312. local tick = 0
  313. while true do
  314. local tickJumps = readInt(2)
  315. if tickJumps == 0 then break end
  316. tick = tick + tickJumps
  317. local layer = 0
  318. while true do
  319. local layerJumps = readInt(2)
  320. if layerJumps == 0 then break end
  321. layer = layer + layerJumps
  322. if layer > track.height then -- nbs can be buggy
  323. for i = track.height + 1, layer do
  324. track.layers[i] = {name = "Layer "..i, volume = 1.0}
  325. track.layers[i].notes = { }
  326. end
  327. track.height = layer
  328. end
  329. local instrument = readInt(1)
  330. local key = readInt(1)
  331. if instrument <= 4 then -- nbs can be buggy
  332. track.layers[layer].notes[tick * 2 - 1] = instrument + 1
  333. track.layers[layer].notes[tick * 2] = key - 33
  334. end
  335. end
  336. end
  337.  
  338. -- Part #3: Layers
  339. for i = 1, track.height do
  340. local name = readStr()
  341. if not name then break end -- if layer data doesnt exist, abort
  342. track.layers[i].name = name
  343. track.layers[i].volume = readInt(1) / 100
  344. end
  345.  
  346. handle.close()
  347. return track
  348. end
  349.  
  350.  
  351.  
  352. function wave.createInstance(track, volume, playing, loop)
  353. volume = volume or 1.0
  354. playing = (playing == nil) or playing
  355. loop = (loop ~= nil) and loop
  356.  
  357. if getmetatable(track).__index == wave.instance then
  358. return track
  359. end
  360. local instance = setmetatable({ }, {__index = wave.instance})
  361. instance.track = track
  362. instance.volume = volume or 1.0
  363. instance.playing = playing
  364. instance.loop = loop
  365. instance.tick = 1
  366. return instance
  367. end
  368.  
  369. function wave.instance:update(interval)
  370. local notes = { }
  371. if self.playing then
  372. local dticks = interval * self.track.tempo
  373. local starttick = self.tick
  374. local endtick = starttick + dticks
  375. local istarttick = math.ceil(starttick)
  376. local iendtick = math.ceil(endtick) - 1
  377. for i = istarttick, iendtick do
  378. for j = 1, self.track.height do
  379. if self.track.layers[j].notes[i * 2 - 1] then
  380. notes[#notes + 1] = self.track.layers[j].notes[i * 2 - 1]
  381. notes[#notes + 1] = self.track.layers[j].notes[i * 2]
  382. notes[#notes + 1] = self.track.layers[j].volume
  383. end
  384. end
  385. end
  386. self.tick = self.tick + dticks
  387.  
  388. if endtick > self.track.length then
  389. self.tick = 1
  390. self.playing = self.loop
  391. end
  392. end
  393. return notes
  394. end
  395.  
  396.  
  397.  
  398. return wave
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement