The arithmetic before the hardware
Sixteen samples. Nine bytes. The ratio is easy to state and easy to get wrong, because the compression is not in the header byte — it is in the nibble width plus a predictive filter that spends four bits per sample and lets the decoder reconstruct the rest from what it already played.
The SNESdev wiki describes the block plainly: BRR is composed of 16-sample blocks, each stored in 9 bytes — a 1-byte control block followed by 8 bytes containing 16 4-bit samples, high nibble first (SNESdev Wiki, BRR samples). The Superfamicom.org article on the format puts the same number another way: a compression ratio of 32:9, so every 32 bytes of 16-bit PCM correspond to 9 bytes of BRR (Superfamicom.org, Bit Rate Reduction).
That 32:9 figure is worth keeping next to the nibble width. Sixteen 16-bit samples are 32 bytes. Sixteen 4-bit samples are 8 bytes. The ninth byte is the header. The saving comes from dropping each sample to four bits and then using the filter to predict what those four bits should be added to. The header is overhead, not the trick.
The nine bytes, offset by offset
The SNESdev wiki lays out the block as a table. Offset 0 is the header. Offsets 1 through 8 hold the samples in nibble pairs, high nibble first:
| Offset | Bits | Meaning |
|---|---|---|
| 0 | SSSS FFLE |
Left-shift (S), decoding filter (F), loop (L), end (E) |
| 1 | AAAA BBBB |
Samples 0 (A) and 1 (B) |
| 2 | CCCC DDDD |
Samples 2 (C) and 3 (D) |
| 3 | EEEE FFFF |
Samples 4 (E) and 5 (F) |
| 4 | GGGG HHHH |
Samples 6 (G) and 7 (H) |
| 5 | IIII JJJJ |
Samples 8 (I) and 9 (J) |
| 6 | KKKK LLLL |
Samples 10 (K) and 11 (L) |
| 7 | MMMM NNNN |
Samples 12 (M) and 13 (N) |
| 8 | OOOO PPPP |
Samples 14 (O) and 15 (P) |
So a decoder reads one byte, extracts four fields by mask and shift, then reads eight more bytes and emits sixteen values. The order matters: the high nibble of each data byte is the earlier sample. A decoder that reads low nibble first will produce a waveform that looks plausible and sounds wrong.
Decoding the header byte
The header is SSSS FFLE. The SNESdev wiki names the fields directly: left-shift (S), decoding filter (F), loop (L), end (E). In code, that is a shift of four, a mask of three, a shift of two, and two single bits.
header = block[0]
shift = (header >> 4) & 0x0F
filter = (header >> 2) & 0x03
loop = (header >> 1) & 0x01
end = header & 0x01
The shift is applied to each nibble before filtering. Each nibble is a signed 4-bit value in the range -8 to +7, and the left-shift value specifies the overall magnitude of the values — it is simply a left shift applied to each sample nibble before BRR filter decoding (SNESdev Wiki).
One practical bound: the Superfamicom.org article notes that a range greater than 12 would shift the nibble past a 16-bit value, so range values from 12 to 15 are invalid (Superfamicom.org). A decoder that accepts those values without comment is not wrong so much as untested.
The four filters and what they trade
Filter 0 uses the shifted nibble directly. The other three add a prediction built from the last one or two decoded samples, each multiplied by a fixed coefficient. The SNESdev wiki gives the table:
| Filter | Shifted nibble | Sample -1 | Sample -2 |
|---|---|---|---|
| 0 | 1 | 0 | 0 |
| 1 | 1 | 15/16 | 0 |
| 2 | 1 | 61/32 | 15/16 |
| 3 | 1 | 115/64 | 13/16 |
Read as a recurrence, filter 1 is out[n] = nibble + (15/16) * out[n-1]. Filter 2 is out[n] = nibble + (61/32) * out[n-1] - (15/16) * out[n-2]. Filter 3 is out[n] = nibble + (115/64) * out[n-1] - (13/16) * out[n-2]. The Superfamicom.org article writes the same three filters in the same form, with the decoded nibble serving as the error term added to the prediction (Superfamicom.org).
The trade is stated on the SNESdev wiki: as the filter number increases, the high-frequency bandwidth (sharpness) of the 4-bit encoding is traded for better low-frequency (smoothness) encoding. The reason is that most natural sounds have stronger low frequencies and weaker high frequencies, so the encoder can adapt the bandwidth allocation per block to the spectrum of the moment.
That is the whole argument for the format in one sentence. Four bits per sample is not enough for a full-bandwidth waveform. Four bits per sample plus a prediction from the last two samples is enough for a waveform whose local spectrum is smooth. The encoder chooses the filter per block; the decoder applies it mechanically.
End, loop, and why the two bits are coupled
The end and loop bits sit next to each other in the header, but they are not independent switches. The SNESdev wiki is explicit: when a block with the end flag set finishes, the channel automatically stops unless the loop flag is also set for that block. If the loop flag is set, the loop point is read based on the channel’s current SCRN and playback continues from there. And the loop flag has no effect on blocks without the end flag set.
Two consequences follow directly from that wording. First, a decoder that treats loop as a standalone flag will appear to work on looped samples and silently misbehave on one-shot samples, because it will try to loop at a block that was never marked as an end. Second, because the block is the smallest unit, loop points must lie on 16-sample boundaries.
The loop address itself is not in the BRR stream. The Superfamicom.org article describes it as part of the sample’s source directory entry: a table of 4-byte entries whose first word is the sample start address and second word is the loop address (Superfamicom.org). The header says whether to loop; the directory says where.
State hygiene: two gotchas with one cause
Two documented behaviors look unrelated until you frame them as the same problem — decoder state that persists across samples.
The first is the filter-0 start. The SNESdev wiki advises that sound samples should normally start with a filter-0 block, to prevent leftover BRR samples from the previously playing sample from having an effect on decoding. Filter 0 ignores the previous samples entirely, so the first block of a new sound cannot inherit the tail of the old one.
The second is the gaussian interpolator. After decoding, playback blends a neighbourhood of 4 adjacent samples according to a lookup table, which reduces high-frequency aliasing across a range of playback rates. The wiki also documents a bug: three maximum-negative values in a row can cause an overflow of the resulting sample, creating a loud pop. That is a property of the interpolator’s arithmetic, not of the BRR block, but it is the same lesson — the decoder and interpolator carry state, and the state has edge cases.
Neither of these is a reason to avoid the format. They are reasons to write a decoder that resets its history at the start of a sample and to test the extreme nibble values deliberately rather than discovering them in a capture.
Where the SPC700 and DSP sit
The BRR stream lives in the SPC700’s address space. The Superfamicom.org SPC700 reference describes the CPU as an 8-bit SPC700 running at 1.024MHz, with each instruction taking a minimum of 2 cycles, and 64KB of memory shared with everything; the DSP provides 8 channels of ADPCM compressed samples (Superfamicom.org, SPC700 reference).
The DSP is reached indirectly. The same reference documents the SPC700-side registers: $F2 holds the DSP register address and $F3 holds the data, so a write is an address to $F2 followed by a value to $F3. The main CPU does not touch these directly; it talks to the SPC700 through the four communication ports at $2140–$2143 on the 65C816 side, mirrored to $F4–$F7 on the SPC700 side.
That indirection is why a BRR trace is readable at all. The sample data is ordinary bytes in SPC RAM; the DSP registers select which source directory entry and which pitch the channel uses. The block format is the part you can decode by hand without a logic analyzer.
A reproducible exercise: decode one block by hand
The format is small enough to verify without special equipment. The following is a proposed exercise, not a reported experiment: it describes what to do, not a result that was obtained.
- Write a 9-byte block by hand. Choose a header with a known shift and filter, for example
0x30— shift 3, filter 0, loop clear, end clear — followed by eight data bytes of your choosing. - Decode it twice: once with the mask-and-shift code above, once by writing out the sixteen nibbles on paper in high-nibble-first order.
- Repeat with filter 1 and the same data bytes. The first sample should be identical; the second should differ by
(15/16) * out[0]. - Place the block in SPC RAM at a known address, point a source directory entry at it, and set the channel’s source number and pitch through
$F2/$F3. - Compare the emulator’s DSP output against your hand decode. A mismatch at sample 0 points at the nibble order or the shift; a mismatch that grows across the block points at the filter recurrence.
The value of the exercise is that every step names a byte, a bit, or a register. If the trace disagrees with the hand decode, one of those named things is wrong, and the disagreement localizes it.
What the format does not tell you
It is worth being precise about the boundary of what the sources support. The SNESdev wiki and the Superfamicom.org article describe the block layout, the header fields, the filter coefficients, the loop/end coupling, and the interpolation caveats. They do not document how any particular commercial sample bank was authored, what filter choices a specific soundtrack made, or what the designers’ intent was in choosing 9 bytes rather than 8 or 10.
Those questions require a different artifact: a disassembly or annotated capture naming the ROM, the sample directory address in SPC RAM, and the BRR block addresses. The format walkthrough above is the prerequisite for reading such a capture, not a substitute for it.
FAQ
Why 9 bytes and not 8?
Because the header carries the shift, filter, loop, and end state for the block. Eight bytes hold the sixteen nibbles; the ninth tells the decoder how to interpret them and what to do when the block ends. The 32:9 ratio in the Superfamicom.org article counts that header.
Can a loop point fall mid-block?
No. The SNESdev wiki states that because of the block structure, loop points must lie on 16-sample boundaries. The loop address in the source directory points at a block, not at a sample within a block.
What happens if the loop bit is set but the end bit is not?
Nothing. The SNESdev wiki states that the loop flag has no effect on blocks without the end flag set. The loop is only consulted when a block marked end finishes.
Why start with filter 0?
Filter 0 ignores the previous decoded samples, so the first block of a new sound cannot be contaminated by the tail of the previous one. The SNESdev wiki recommends this explicitly.
Is the gaussian overflow bug a BRR bug?
No. It is a bug in the DSP’s gaussian interpolation filter, documented on the SNESdev wiki: three maximum-negative values in a row can overflow the resulting sample and create a loud pop. The BRR block decodes correctly; the interpolator is where the overflow happens.
Sources
- SNESdev Wiki, “BRR samples” — block layout, header fields, filter coefficient table, loop/end coupling, filter-0 start recommendation, gaussian interpolation and overflow bug.
- Superfamicom.org, “Bit Rate Reduction (BRR)” — 32:9 compression ratio, header bit diagram, range validity bound, source directory entry layout.
- Superfamicom.org, “SPC700 reference” — SPC700 clock and memory, DSP access through
$F2/$F3, communication ports$2140–$2143and$F4–$F7.