GRxRedZero

Untitled

Apr 26th, 2017
74
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.43 KB | None | 0 0
  1. -- Noteblock Studio file (.nbs) player for MiscPeripherals (a ComputerCraft mod)
  2. -- version 2
  3. -- You are free to copy, alter, and distribute this under the following conditions:
  4. -- 1) You leave these comments and credits attached
  5. -- 2) You do not make money off of this
  6. -- Program by James0x57 - http://www.youtube.com/watch?v=ro4EK8smULQ
  7. -- Noteblock Studio by _Davve_ - http://www.minecraftforum.net/topic/136749-minecraft-note-block-studio-150000-downloads/
  8. -- MiscPeripherals by RichardG867 - http://www.computercraft.info/forums2/index.php?/topic/4587-cc1481mc146-miscperipherals-23/
  9. -- ComputerCraft by Dan - http://www.computercraft.info/
  10. -- If you use this in anything you create, please show credit where due, and let me know because I'd love to see it used!! =)
  11.  
  12. local args = {...};
  13. local menu = {};
  14. local box = nil;
  15. local song = nil;
  16. local action = 'mainList';
  17. local isPlaying = false;
  18. local stopFlag = false;
  19. local settings = { rptMode = "allRandom"; }; -- "repeat" is reserved in lua
  20. local maxtoshow = 15;
  21.  
  22. if turtle ~= nil then
  23. maxtoshow = 9;
  24. end
  25.  
  26. function mountIronNoteblock()
  27. for _,side in ipairs({"left"; "right"; "back"; "bottom"; "top"; "front"}) do
  28. if peripheral.getType(side) == "note" then
  29. box = peripheral.wrap(side);
  30. return side;
  31. end
  32. end
  33. return nil;
  34. end
  35.  
  36. function newSong(x, selectedIndex)
  37. return {
  38. fh = x;
  39. index = selectedIndex;
  40. length = 0;
  41. height = 0;
  42. name = "";
  43. author = "";
  44. originalAuthor = "";
  45. description = "";
  46. tempo = 10.00; --tks per second
  47. autoSaving = 0;
  48. autoSaveDur = 0;
  49. timeSig = 4;
  50. minSpent = 0;
  51. leftClicks = 0;
  52. rightClicks = 0;
  53. blocksAdded = 0;
  54. blocksRemoved = 0;
  55. midi = "";
  56. music = { wait={}; inst={}; note={}; };
  57. };
  58. end
  59.  
  60. function loadMenu(fromDir)
  61. if fs.isDir(fromDir) then
  62. for _, file in ipairs(fs.list(fromDir)) do
  63. if fs.isDir(file) == false and string.find(file, ".nbs", -4, true) ~= nil then -- if file and ends in ".nbs"
  64. menu[#menu+1] = { d=fromDir; fn=file };
  65. end
  66. end
  67. end
  68. return #menu;
  69. end
  70.  
  71. function readInt(fh) --little endian, fh is open in rb mode
  72. local ret = 0;
  73. local x = fh.read();
  74. if x == nil then return nil; end
  75. ret = x;
  76. x = fh.read();
  77. if x == nil then return nil; end
  78. ret = (x * 0x100) + ret;
  79. x = fh.read();
  80. if x == nil then return nil; end
  81. ret = (x * 0x10000) + ret;
  82. x = fh.read();
  83. if x == nil then return nil; end
  84. ret = (x * 0x1000000) + ret;
  85. return ret;
  86. end
  87.  
  88. function readShort(fh) --little endian, fh is open in rb mode
  89. local ret = 0;
  90. local x = fh.read();
  91. if x == nil then return nil; end
  92. ret = x;
  93. x = fh.read();
  94. if x == nil then return nil; end
  95. ret = (x * 0x100) + ret;
  96. return ret;
  97. end
  98.  
  99. function readString(fh, len) --fh is open in rb mode
  100. local ret = "";
  101. local x = 0;
  102. for i = 1, len do
  103. x = fh.read();
  104. if x == nil then return nil; end
  105. ret = ret .. string.char(x);
  106. end
  107. return ret;
  108. end
  109.  
  110. function readHeader()
  111. song.length = readShort(song.fh);
  112. song.height = readShort(song.fh);
  113. song.name = readString(song.fh, readInt(song.fh));
  114. song.author = readString(song.fh, readInt(song.fh));
  115. song.originalAuthor = readString(song.fh, readInt(song.fh));
  116. song.description = readString(song.fh, readInt(song.fh));
  117. song.tempo = 1.000 / ( readShort(song.fh) / 100.00 );
  118. song.autoSaving = song.fh.read();
  119. song.autoSaveDur = song.fh.read();
  120. song.timeSig = song.fh.read();
  121. song.minSpent = readInt(song.fh);
  122. song.leftClicks = readInt(song.fh);
  123. song.rightClicks = readInt(song.fh);
  124. song.blocksAdded = readInt(song.fh);
  125. song.blocksRemoved = readInt(song.fh);
  126. song.midi = readString(song.fh, readInt(song.fh));
  127. end
  128.  
  129. function readNotes()
  130. local curtk = 1;
  131. local tk = -1;
  132. local layer = -1;
  133. local inst = 0;
  134. local note = 33; -- MC is 33 to 57
  135.  
  136. while true do
  137. tk = readShort(song.fh);
  138. if tk == nil then return false; end
  139. if tk == 0 then break; end
  140. while true do
  141. song.music.wait[curtk] = (tk * song.tempo) * 0.965; -- * 0.965 to speed it up a bit because lua slow
  142. layer = readShort(song.fh); --can't do anything with this info (yet?)
  143. if layer == nil then return false; end
  144. if layer == 0 then break; end
  145. song.music.inst[curtk]=song.fh.read();
  146. if song.music.inst[curtk] == 0 then
  147. song.music.inst[curtk] = 0;
  148. elseif song.music.inst[curtk] == 2 then
  149. song.music.inst[curtk] = 1;
  150. elseif song.music.inst[curtk] == 3 then
  151. song.music.inst[curtk] = 2;
  152. elseif song.music.inst[curtk] == 4 then
  153. song.music.inst[curtk] = 3;
  154. elseif song.music.inst[curtk] == 1 then
  155. song.music.inst[curtk] = 4;
  156. end
  157. song.music.note[curtk]=song.fh.read()-33;
  158. tk = 0;
  159. curtk = curtk + 1;
  160. end
  161. end
  162. return true;
  163. end
  164.  
  165. function showInfo()
  166. term.clear();
  167. print("Now Playing: \n\n\n\n\n\n " .. song.name);
  168. print("\n\n\n\n\n\nAuthor: " .. song.author);
  169. print("Original Author: " .. song.originalAuthor);
  170. print("Description: ");
  171. print(song.description);
  172. parallel.waitForAny(function()
  173. _, key = os.pullEvent("key");
  174. end, function()
  175. while true do
  176. if not isPlaying then break; end
  177. os.sleep(0.125);
  178. end
  179. end);
  180. if settings.rptMode == "none" or isPlaying then --song finished in single play mode or key pressed exit
  181. action = 'mainList';
  182. end
  183. end
  184.  
  185. function getRepeateMode() -- returns the name of currently selected repeat mode
  186. local rptText = "";
  187.  
  188. if settings.rptMode == "allRandom" then
  189. rptText = "All (Random)";
  190. elseif settings.rptMode == "allOrdered" then
  191. rptText = "All (In Order)";
  192. elseif settings.rptMode == "one" then
  193. rptText = "One (Loop Song)";
  194. elseif settings.rptMode == "none" then
  195. rptText = "None";
  196. end
  197.  
  198. return rptText;
  199. end
  200.  
  201. function changeRepeatMode() -- cycles to next repeat mode and returns its name
  202. if settings.rptMode == "allRandom" then
  203. settings.rptMode = "allOrdered";
  204. elseif settings.rptMode == "allOrdered" then
  205. settings.rptMode = "one";
  206. elseif settings.rptMode == "one" then
  207. settings.rptMode = "none";
  208. elseif settings.rptMode == "none" then
  209. settings.rptMode = "allRandom";
  210. end
  211.  
  212. return getRepeateMode();
  213. end
  214.  
  215. function options()
  216. local selectedIndex = 1;
  217. while true do
  218. term.clear();
  219. local opts = {};
  220. opts[1] = "Show Now Playing";
  221. opts[2] = "Repeat: " .. getRepeateMode();
  222. opts[3] = "Next Song";
  223. opts[4] = "Stop";
  224. opts[5] = "Back To Song List";
  225. -- "Load Playlist" --> default song lists & any saved lists
  226. -- "Add " .. selected main menu song .. " to a playlist"
  227. -- "Queue " .. selected main menu song -- takes priority over repeat mode
  228. -- "Load songs from ..." -- prompts for folder path, erases current queue
  229.  
  230. for i = 1, #opts do
  231. if i == selectedIndex then
  232. print("> " .. opts[i]);
  233. else
  234. print(" " .. opts[i]);
  235. end
  236. end
  237. print("----------------------\nUse Arrow keys and Enter to navigate.\n");
  238. _, key = os.pullEvent("key");
  239.  
  240. if key == 208 or key == 31 then -- down or s
  241. selectedIndex = selectedIndex + 1;
  242. if selectedIndex > #opts then selectedIndex = 1; end
  243. elseif key == 200 or key == 17 then -- up or w
  244. selectedIndex = selectedIndex-1;
  245. if selectedIndex < 1 then selectedIndex = 1; end
  246. elseif key == 28 or key == 57 then -- enter or space
  247. if selectedIndex == 1 and isPlaying then
  248. action = 'nowPlaying';
  249. break;
  250. elseif selectedIndex == 2 then
  251. changeRepeatMode();
  252. elseif selectedIndex == 3 then
  253. skipSong();
  254. break;
  255. elseif selectedIndex == 4 then
  256. stopSong();
  257. action = 'mainList';
  258. break;
  259. else
  260. action = 'mainList';
  261. break;
  262. end
  263. end
  264. end
  265. end
  266.  
  267. function mainList(startat, selectedIndex)
  268. term.clear();
  269. for i = startat, #menu do
  270. if startat + maxtoshow <= i then break end
  271. if i == selectedIndex then
  272. print("> " .. menu[i].fn);
  273. else
  274. print(" " .. menu[i].fn);
  275. end
  276. end
  277. print("----------------------\nUse Arrow keys and Enter to navigate.");
  278. print("Press m to access options.")
  279. _, key = os.pullEvent("key");
  280.  
  281. if key == 208 or key == 31 then -- down or s
  282. selectedIndex = selectedIndex + 1;
  283. if selectedIndex >= startat + maxtoshow then startat = startat + maxtoshow; end
  284. if selectedIndex > #menu then selectedIndex = 1; startat = 1; end
  285. elseif key == 200 or key == 17 then -- up or w
  286. selectedIndex = selectedIndex-1;
  287. if selectedIndex < 1 then selectedIndex = 1; end
  288. if selectedIndex < startat then startat = startat - maxtoshow; end
  289. if startat < 1 then startat = 1; end
  290. elseif key == 205 or key == 32 then -- right or d
  291. selectedIndex = startat + maxtoshow;
  292. if selectedIndex > #menu then selectedIndex = 1; end
  293. startat = selectedIndex;
  294. elseif key == 203 or key == 30 then -- left or a
  295. selectedIndex = startat - maxtoshow;
  296. startat = startat - maxtoshow;
  297. if selectedIndex < 1 then selectedIndex = 1; end
  298. if startat < 1 then startat = 1; end
  299. elseif key == 28 or key == 57 then -- enter or space
  300. action = 'playSong';
  301. elseif key == 50 or key == 19 then -- m or r
  302. action = 'options';
  303. end
  304.  
  305. return startat, selectedIndex;
  306. end
  307.  
  308. function continueWith() -- returns a menu index of the next song based on the selected repeat mode
  309. local contWith = 1;
  310. if #menu > 0 then
  311. if settings.rptMode == "allRandom" then
  312. contWith = math.random(#menu);
  313. elseif settings.rptMode == "allOrdered" then
  314. contWith = song.index + 1;
  315. if contWith > #menu then
  316. contWith = 1;
  317. end
  318. elseif settings.rptMode == "one" then
  319. contWith = song.index;
  320. end
  321. end
  322. return contWith;
  323. end
  324.  
  325. function playNotes(doReturn)
  326. while true do
  327. if action == 'songReady' then
  328. isPlaying = true;
  329. action = 'nowPlaying';
  330. os.queueEvent("playStarted");
  331. for i = 1, #song.music.wait - 1 do
  332. if song.music.wait[i] ~= 0 then
  333. os.sleep(song.music.wait[i]);
  334. if stopFlag then break; end
  335. end
  336. pcall(box.playNote, song.music.inst[i], song.music.note[i]);
  337. end
  338. isPlaying = false;
  339. os.queueEvent("playEnded");
  340. if not stopFlag then --song finished (instead of controller terminated)
  341. if #menu > 0 and settings.rptMode ~= "none" then
  342. menuAt(continueWith()); --continue playing songs based on current repeat mode
  343. end
  344. end
  345. end
  346. if doReturn ~= nil and doReturn == true then break; end
  347. os.sleep(0.25);
  348. end
  349. end
  350.  
  351. function stopSong()
  352. if isPlaying then
  353. stopFlag = true;
  354. parallel.waitForAny(function()
  355. while true do
  356. if not isPlaying then break; end
  357. os.sleep(0.125);
  358. end
  359. end);
  360. stopFlag = false;
  361. end
  362. end
  363.  
  364. function menuAt(x) -- plays the song on the menu at index x
  365. stopSong();
  366. song = newSong(fs.open(menu[x].d .. "/" .. menu[x].fn, "rb"), x);
  367. readHeader();
  368. readNotes();
  369. song.fh.close();
  370. action = 'songReady';
  371. end
  372.  
  373. function skipSong()
  374. if #menu > 0 and settings.rptMode ~= "none" then
  375. menuAt(continueWith()); --continue playing songs based on current repeat mode
  376. else
  377. stopSong();
  378. end
  379. end
  380.  
  381. function controller() --handles actions
  382. local startat = 1;
  383. local selectedIndex = 1;
  384. while true do
  385. if action == 'mainList' then
  386. startat, selectedIndex = mainList(startat, selectedIndex);
  387. elseif action == 'playSong' then
  388. menuAt(selectedIndex);
  389. action = 'songReady';
  390. elseif action == 'songReady' then
  391. os.sleep(0.125);
  392. elseif action == 'nowPlaying' then
  393. showInfo();
  394. elseif action == 'options' then
  395. options();
  396. end
  397. end
  398. end
  399.  
  400. function clearMenu()
  401. menu = {};
  402. end
  403.  
  404. function menuTable() -- returns all the loaded songs that would show up in the menu
  405. return menu;
  406. end
  407.  
  408. function launchUI()
  409. if box == nil then
  410. print("No Iron Noteblock Detected");
  411. return;
  412. end
  413.  
  414. if args[1] == nil or fs.isDir(args[1]) == false then
  415. args[1] = "songs";
  416. if fs.isDir("songs") == false then
  417. fs.makeDir("songs");
  418. end
  419. elseif args[1] ~= nil and fs.isDir(args[1]) == true then
  420. loadMenu(args[1]);
  421. end
  422.  
  423. clearMenu();
  424. loadMenu("songs");
  425. loadMenu("rom/songs"); -- \Desktop\MindCrack\minecraft\mods\ComputerCraft\lua\rom\songs (ComputerCraft folder inside \mods\ must be created, it is not there by default)
  426.  
  427. parallel.waitForAll(playNotes, controller);
  428. end
  429.  
  430. function current() -- returns convinient 'song' table with complete header info, notes, and etc, --OR-- nil if nothing is playing (useful)
  431. if isPlaying == true then
  432. return song; --full song table, has everything
  433. else
  434. return nil;
  435. end
  436. end
  437.  
  438. mountIronNoteblock();
  439. if shell ~= nil then --ran normally, not loaded as API
  440. launchUI();
  441. else --else it was loaded as an api so don't do anything else automatically
  442. settings.rptMode = "none";
  443. end
Add Comment
Please, Sign In to add comment