Advertisement
hhhzzzsss

badapple.lua

Mar 23rd, 2025 (edited)
506
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 3.87 KB | None | 0 0
  1. local component = require("component")
  2. local event = require("event")
  3. local filesystem = require("filesystem")
  4. local bit32 = require("bit32")
  5. local os = require("os")
  6. local modem = component.modem
  7.  
  8. local CONTROLLER_PORT = 1
  9. local WORKER_PORT = 2
  10. local PING_TIMEOUT = 20
  11. local AUDIO_FILE_PATH = "/home/badapple.audio"  -- Path to the encoded audio file
  12.  
  13. local function tickTime()
  14.     return os.time() * (1000/60/60)
  15. end
  16.  
  17. -- Function to convert MIDI pitch to frequency in Hz
  18. local function midiToFrequency(midiNote)
  19.     return 440 * 2 ^ ((midiNote - 69) / 12)
  20. end
  21.  
  22. -- Function to read a variable-length integer from the file
  23. local function readVarInt(file)
  24.     local value = 0
  25.     local shift = 0
  26.     while true do
  27.         local byte = file:read(1)
  28.         if not byte then return nil end
  29.         byte = string.byte(byte)
  30.         value = value + bit32.lshift(bit32.band(byte, 0x7F), shift)
  31.         if bit32.band(byte, 0x80) == 0 then break end
  32.         shift = shift + 7
  33.     end
  34.     return value
  35. end
  36.  
  37. -- Step 1: Broadcast a ping to discover worker addresses
  38. modem.open(WORKER_PORT)
  39.  
  40. local function discoverWorkers()
  41.     local addresses = {}
  42.  
  43.     modem.broadcast(CONTROLLER_PORT, "ping")
  44.  
  45.     local deadline = tickTime() + PING_TIMEOUT
  46.     while tickTime() < deadline do
  47.         local _, _, from, port = event.pull((deadline - tickTime()) / 20, "modem_message")
  48.         if port == WORKER_PORT then
  49.             table.insert(addresses, from)
  50.         end
  51.     end
  52.  
  53.     return addresses
  54. end
  55.  
  56. local workerAddresses = discoverWorkers()
  57.  
  58. if #workerAddresses == 0 then
  59.     print("No workers found. Exiting...")
  60.     return
  61. end
  62.  
  63. -- Initialize worker availability tracking
  64. local workerAvailability = {}
  65. for _, address in ipairs(workerAddresses) do
  66.     workerAvailability[address] = 0  -- All workers are initially free
  67. end
  68.  
  69. -- Step 2: Read the encoded audio file and play notes
  70. if not filesystem.exists(AUDIO_FILE_PATH) then
  71.     print("Audio file not found: " .. AUDIO_FILE_PATH)
  72.     return
  73. end
  74.  
  75. local file, err = io.open(AUDIO_FILE_PATH, "rb")
  76. if not file then
  77.     print("Failed to open audio file: " .. err)
  78.     return
  79. end
  80.  
  81. local referenceTick = tickTime()
  82.  
  83. while true do
  84.     -- Read deltaTick
  85.     local deltaTick = readVarInt(file)
  86.     if not deltaTick then break end
  87.  
  88.     -- Read duration
  89.     local durationTicks = readVarInt(file)
  90.     if not durationTicks then break end
  91.  
  92.     -- Read pitch
  93.     local pitchByte = file:read(1)
  94.     if not pitchByte then break end
  95.     local pitch = string.byte(pitchByte)
  96.  
  97.     -- Wait until desired time
  98.     referenceTick = referenceTick + deltaTick
  99.     while tickTime() < referenceTick do
  100.         os.sleep(0.01)  -- Sleep to avoid busy waiting
  101.     end
  102.  
  103.     -- Convert pitch to frequency and duration to seconds
  104.     local frequency = midiToFrequency(pitch)
  105.     local durationSeconds = durationTicks * 0.05
  106.  
  107.     -- Find the earliest available worker
  108.     local currentTick = tickTime()
  109.     local earliestFreeTime = math.huge
  110.     local selectedWorker = nil
  111.     for address, freeTime in pairs(workerAvailability) do
  112.         if freeTime < earliestFreeTime then
  113.             earliestFreeTime = freeTime
  114.             selectedWorker = address
  115.         end
  116.     end
  117.  
  118.     if selectedWorker and workerAvailability[selectedWorker] <= currentTick then
  119.         -- Send the note to the selected worker
  120.         modem.send(selectedWorker, CONTROLLER_PORT, "note", frequency, durationSeconds - 0.1)
  121.         -- Update the worker's next free time
  122.         workerAvailability[selectedWorker] = currentTick + math.max(durationTicks, 3)
  123.     else
  124.         -- No workers are currently free; note cannot be played
  125.         print(string.format("Note at tick %.2f (frequency: %.2f Hz, duration: %.2f s) could not be played; all workers are busy.", currentTick, frequency, durationSeconds))
  126.     end
  127. end
  128.  
  129. file:close()
  130. print("Playback finished.")
  131.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement