Audio Sensor
An audio sensor hears what a player standing at its block would hear, and reports each sound: which one, and where it came from.
Facts
| | |
|—|—|
| Type | hearing |
| Address | An expansion slot path such as exp:0, or a hub’s |
| Range | The game’s own range for each sound |
| Interrupt | Raised while events wait |
| Power | 9 W |
| Info | kind (audio_sensor) |
It reports the sound the game plays, by its id, such as minecraft:entity.pig.ambient, and nothing
else about it (no category, no mob). It is exactly what the game plays: a skeleton’s step is
minecraft:entity.skeleton.step, and the stone under it is silent. It hears every sound the server
sends to players, and the sounds players make of world events (a block broken plays its own break
sound). A sound counts if a player at the sensor’s block would hear it. It works while a running
computer powers it.
In mcos
/dev/hearing0 (hearing1 for the next) is a read-only file, an event a line:
minecraft:entity.pig.ambient 1.5 0.0 -3.0
That is sound x y z. A read with nothing waiting waits for the next event, and poll reports the
file readable (IN) while one waits. When events were dropped, a line lost n comes before the
next one. Opening it to write fails with EACCES.
tail -f /dev/hearing0
The file’s ioctls are the sensor’s methods, by name, in any open mode:
sys.ioctl(fd, "pull") answers the oldest event as a table, with numbers for its coordinates. A
sensor that is gone answers ENODEV. setLabel labels it, as for any device (see
The sys library).
The file and pull take from the same buffer, in order: an event read from the file is gone from
pull, and the other way round. Polling loses nothing: an event a poll found waiting is the next
one pull answers (or the next line read), so a program can wait with sys.poll and then take
events with pull until it answers nil.
Methods
pull() → event or nil
The oldest sound heard and waiting, as an event table (see Events), or nil when none waits. Each is taken once.
local event = sys.ioctl(fd, "pull") -- {sound = "minecraft:entity.pig.ambient", x = 1.5, y = 0.0, z = -3.0}
Events
| Field | Value |
|---|---|
sound |
the sound’s id |
x, y, z |
where it came from, relative to the block’s center, in blocks, to one decimal place |
lost |
present when events were dropped: how many |
Each sound heard raises the sensor’s interrupt, which stays raised while any wait. At most 32 wait. When more come, the oldest goes and the next event carries lost. Events are kept
across a save, and dropped while no program runs. The directions are the world’s. A
sound card starting to play is heard once at its start.
Examples
Run these with lua name.lua &, from a computer with an audio sensor. For why they loop without
yielding, see Writing programs.
Count the first five sounds
local f = io.open("/dev/hearing0")
for i = 1, 5 do
print(i, f:read("l")) -- waits for each sound
end
Log a base’s doors and chests
-- Log the sounds of a base's doors, chests and broken blocks: each sound the audio sensor hears
-- whose id matches one of WATCH is appended to a log with the world's time and where it came from
-- ("Day 2 14:03:10 minecraft:block.chest.open 2.5 0.0 -1.5").
--
-- It reads the sensor's file, a line a sound: read blocks until one is heard. The log is opened and
-- closed for each line, so it is on the disk at once (a disk's file shows its writes at close).
local LOG = "/home/sounds.log"
local WATCH = { -- Lua patterns over a sound's id
"door%.", -- any door or trapdoor opening or closing
"^minecraft:block%.chest%.",
"^minecraft:block%.barrel%.",
"%.break$", -- a block broken
}
local function watched(sound)
for _, pattern in ipairs(WATCH) do
if sound:find(pattern) then
return true
end
end
return false
end
local sensor = assert(io.open("/dev/hearing0"))
for line in sensor:lines() do
local sound = line:match("^(%S+) %S+ %S+ %S+$")
if sound and watched(sound) then
local log = assert(io.open(LOG, "a"))
log:write(os.date("%c"), " ", line, "\n")
log:close()
elseif line:match("^lost ") then
local log = assert(io.open(LOG, "a"))
log:write(os.date("%c"), " (", line, " sounds)\n")
log:close()
end
end
The patterns are Lua’s: %. is a dot. tail -f /home/sounds.log follows the log.
Point toward the last sound
-- Point at the last sound: twice a second, the redstone card lights the one face that looks most
-- toward the last sound the audio sensor heard (east, west, up, down, south or north), and darkens
-- the others. Put lamps on the block's faces and they show where the noise is.
--
-- It uses the sensor's method, pull, through its file's ioctls (each sound a table, its x, y and z
-- numbers: x grows east, y up, z south), and the redstone card's set, every face in one call.
local sys = require("sys")
local LOOK = 0.5 -- seconds between looks
local sensor = assert(sys.open("/dev/hearing0"))
local card = assert(sys.open("/dev/redstone0"))
-- The face toward a direction: the axis it is most along.
local function toward(x, y, z)
local ax, ay, az = math.abs(x), math.abs(y), math.abs(z)
if ax >= ay and ax >= az then
return x >= 0 and "east" or "west"
elseif ay >= az then
return y >= 0 and "up" or "down"
end
return z >= 0 and "south" or "north"
end
local lit
while true do
local last
local sound = sys.ioctl(sensor, "pull")
while sound do -- (the newest one heard since the last look)
last = sound
sound = sys.ioctl(sensor, "pull")
end
if last then
local face = toward(last.x, last.y, last.z)
if face ~= lit then
local faces = {}
for _, name in ipairs({"north", "south", "east", "west", "up", "down"}) do
faces[name] = {powered = name == face and 15 or 0}
end
sys.ioctl(card, "set", faces)
lit = face
end
end
sys.sleep(LOOK)
end
It needs a redstone card in the same computer. The directions are the world’s, so the faces it lights are too, however the computer is turned.