CCCPaste

CC NBS player

-- simple_nbs_player.lua
-- Minimal NBS player for CraftOS 1.7 + OpenPeripheral note blocks

local nb = peripheral.wrap("right") -- change side to your note block
if not nb then
    print("No note block peripheral found!")
    return
end

-- Load .nbs file (binary)
local function loadNBS(filename)
    local f = fs.open(filename, "rb")
    if not f then return nil end
    local data = f.readAll()
    f.close()
    return data
end

-- Convert bytes to little-endian 16-bit integer
local function readShort(data, pos)
    local a = string.byte(data, pos)
    local b = string.byte(data, pos+1)
    return a + b*256
end

-- Simple parsing for minimal NBS (version 3–5)
local function parseNBS(data)
    local pos = 8 -- skip header (short length + version info)
    local tempo = readShort(data, pos) / 100 -- ticks per beat
    pos = pos + 2

    local notes = {} -- {tick = {{layer, instrument, key}, ...}}

    local tick = 0
    while pos < #data do
        local jumpTicks = readShort(data, pos)
        pos = pos + 2
        if jumpTicks == 0 then break end
        tick = tick + jumpTicks

        local layer = 0
        while true do
            local jumpLayers = readShort(data, pos)
            pos = pos + 2
            if jumpLayers == 0 then break end
            layer = layer + jumpLayers

            local instrument = string.byte(data, pos)
            local key = string.byte(data, pos+1)
            pos = pos + 2

            if not notes[tick] then notes[tick] = {} end
            table.insert(notes[tick], {layer=layer, instrument=instrument, key=key})
        end
    end

    return notes, tempo
end

-- Map NBS instruments to Minecraft sounds
local instrumentMap = {
    [0] = "note.harp",
    [1] = "note.bass",
    [2] = "note.snare",
    [3] = "note.hat",
    [4] = "note.bassattack"
}

-- Convert NBS key to pitch (rough approximation)
local function keyToPitch(key)
    return 2 ^ ((key - 45) / 12)
end

-- Play parsed notes
local function playNBS(notes, tempo)
    local sortedTicks = {}
    for tick in pairs(notes) do table.insert(sortedTicks, tick) end
    table.sort(sortedTicks)

    for _, tick in ipairs(sortedTicks) do
        local tickNotes = notes[tick]
        for _, n in ipairs(tickNotes) do
            local sound = instrumentMap[n.instrument] or "note.harp"
            local pitch = keyToPitch(n.key)
            nb.playSound(sound, 1, pitch)
        end
        sleep(tempo) -- wait until next tick
    end
end

-- ===== Main =====
local filename = "song.nbs"
local data = loadNBS(filename)
if not data then
    print("Failed to load "..filename)
    return
end

local notes, tempo = parseNBS(data)
print("Playing "..filename.." at tempo "..tempo)
playNBS(notes, tempo)
print("Done!")