Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local event = require("event")
- local filesystem = require("filesystem")
- local bit32 = require("bit32")
- local os = require("os")
- local modem = component.modem
- local CONTROLLER_PORT = 1
- local WORKER_PORT = 2
- local PING_TIMEOUT = 20
- local AUDIO_FILE_PATH = "/home/badapple.audio" -- Path to the encoded audio file
- local function tickTime()
- return os.time() * (1000/60/60)
- end
- -- Function to convert MIDI pitch to frequency in Hz
- local function midiToFrequency(midiNote)
- return 440 * 2 ^ ((midiNote - 69) / 12)
- end
- -- Function to read a variable-length integer from the file
- local function readVarInt(file)
- local value = 0
- local shift = 0
- while true do
- local byte = file:read(1)
- if not byte then return nil end
- byte = string.byte(byte)
- value = value + bit32.lshift(bit32.band(byte, 0x7F), shift)
- if bit32.band(byte, 0x80) == 0 then break end
- shift = shift + 7
- end
- return value
- end
- -- Step 1: Broadcast a ping to discover worker addresses
- modem.open(WORKER_PORT)
- local function discoverWorkers()
- local addresses = {}
- modem.broadcast(CONTROLLER_PORT, "ping")
- local deadline = tickTime() + PING_TIMEOUT
- while tickTime() < deadline do
- local _, _, from, port = event.pull((deadline - tickTime()) / 20, "modem_message")
- if port == WORKER_PORT then
- table.insert(addresses, from)
- end
- end
- return addresses
- end
- local workerAddresses = discoverWorkers()
- if #workerAddresses == 0 then
- print("No workers found. Exiting...")
- return
- end
- -- Initialize worker availability tracking
- local workerAvailability = {}
- for _, address in ipairs(workerAddresses) do
- workerAvailability[address] = 0 -- All workers are initially free
- end
- -- Step 2: Read the encoded audio file and play notes
- if not filesystem.exists(AUDIO_FILE_PATH) then
- print("Audio file not found: " .. AUDIO_FILE_PATH)
- return
- end
- local file, err = io.open(AUDIO_FILE_PATH, "rb")
- if not file then
- print("Failed to open audio file: " .. err)
- return
- end
- local referenceTick = tickTime()
- while true do
- -- Read deltaTick
- local deltaTick = readVarInt(file)
- if not deltaTick then break end
- -- Read duration
- local durationTicks = readVarInt(file)
- if not durationTicks then break end
- -- Read pitch
- local pitchByte = file:read(1)
- if not pitchByte then break end
- local pitch = string.byte(pitchByte)
- -- Wait until desired time
- referenceTick = referenceTick + deltaTick
- while tickTime() < referenceTick do
- os.sleep(0.01) -- Sleep to avoid busy waiting
- end
- -- Convert pitch to frequency and duration to seconds
- local frequency = midiToFrequency(pitch)
- local durationSeconds = durationTicks * 0.05
- -- Find the earliest available worker
- local currentTick = tickTime()
- local earliestFreeTime = math.huge
- local selectedWorker = nil
- for address, freeTime in pairs(workerAvailability) do
- if freeTime < earliestFreeTime then
- earliestFreeTime = freeTime
- selectedWorker = address
- end
- end
- if selectedWorker and workerAvailability[selectedWorker] <= currentTick then
- -- Send the note to the selected worker
- modem.send(selectedWorker, CONTROLLER_PORT, "note", frequency, durationSeconds - 0.1)
- -- Update the worker's next free time
- workerAvailability[selectedWorker] = currentTick + math.max(durationTicks, 3)
- else
- -- No workers are currently free; note cannot be played
- print(string.format("Note at tick %.2f (frequency: %.2f Hz, duration: %.2f s) could not be played; all workers are busy.", currentTick, frequency, durationSeconds))
- end
- end
- file:close()
- print("Playback finished.")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement