Ring Buffers for Continuous Event Detection
When you’re sitting with a contact microphone taped to a mushroom cap, recording for 11 hours straight hoping to catch the faint sound of spore discharge, you face a storage problem: you can’t keep everything (that’s 950 MB per hour at 24-bit/44.1kHz), but you also can’t discard everything (you might miss the event). You need to keep a sliding window—the most recent few seconds—so when something interesting happens, you can save the context around it.
Ring buffers solve this by treating fixed-size memory as circular: when you reach the end, wrap back to the start and overwrite the oldest data. The buffer always holds the N most recent samples, and the write pointer just keeps spinning.
Here’s Elixir showing a ring buffer as a recursive process with a fixed-size list:
defmodule RingBuffer do
def start(size), do: spawn(fn -> loop([], size, size) end)
defp loop(buf, size, space) do
receive do
{:write, val} when space > 0 -> loop([val | buf], size, space - 1)
{:write, val} -> loop([val | Enum.drop(buf, -1)], size, 0)
{:read, pid} -> send(pid, Enum.reverse(buf)); loop(buf, size, space)
end
end
end
buf = RingBuffer.start(3)
for x <- 1..5, do: send(buf, {:write, x})
send(buf, {:read, self()})
receive do
contents -> IO.inspect(contents) # [3, 4, 5]
end
Bash can do the same with an array and modulo arithmetic:
BUF=() SIZE=4 POS=0
write() { BUF[$((POS % SIZE))]=$1; ((POS++)); }
read_buf() { for ((i=0; i<SIZE; i++)); do echo "${BUF[i]}"; done; }
for x in {1..7}; do write $x; done
read_buf # outputs: 5 6 7 4 (indices 0-3, wrapped)
The buffer doesn’t care that you wrote seven values. It remembers the last four, indices cycling 0→1→2→3→0. When your recording software detects a spike above threshold, it dumps the ring buffer to disk: three seconds before the event, the event itself, two seconds after. The surrounding silence matters—it’s proof the spike wasn’t building vibration or thermal expansion.