{
  "path": "/3mkjx5kshds2s",
  "site": "at://did:plc:7vdlgi2bflelz7mmuxoqjfcr/site.standard.publication/3maigovix422i",
  "tags": [],
  "$type": "site.standard.document",
  "title": "Rockbox -> Snapcast : FIFO & TCP PCM Sinks",
  "content": {
    "$type": "pub.leaflet.content",
    "pages": [
      {
        "id": "019dd2b9-4b25-7dd4-8edd-14bf9c422825",
        "$type": "pub.leaflet.pages.linearDocument",
        "blocks": [
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "This document traces every hop an audio frame takes from the Rockbox C firmware through the Snapcast PCM sinks to a Snapcast server."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Two complementary sinks are available:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 35,
                          "byteStart": 14
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 68,
                          "byteStart": 61
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "FIFO / pipe | audio_output = \"fifo\" | Named FIFO or stdout | pipe:// "
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 44,
                          "byteStart": 15
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 66,
                          "byteStart": 60
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "TCP (direct) | audio_output = \"snapcast_tcp\" | TCP socket | tcp:// |"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 13,
                    "byteStart": 4
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 51,
                    "byteStart": 43
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 110,
                    "byteStart": 102
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "The FIFO sink is the traditional approach: rockboxd writes to a named pipe that snapserver reads. The TCP sink connects directly to snapserver's TCP source port — no FIFO, no filesystem dependency, auto-discoverable via mDNS."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Table of contents"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.orderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 8,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "#overview",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Overview"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 20,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "#choosing-fifo-vs-tcp",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Choosing FIFO vs TCP"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 9,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "#fifo-sink",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "FIFO sink"
                  },
                  "unorderedListChildren": {
                    "$type": "pub.leaflet.blocks.unorderedList",
                    "children": [
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 9,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#fifo-layer-map",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "Layer map"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 15,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#pcm-sink-vtable-pcm-fifoc",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "PCM sink vtable"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 14,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#the-dma-thread",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "The DMA thread"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 22,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#fifo-pre-open-strategy",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "FIFO pre-open strategy"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 11,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#stdout-mode",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "stdout mode"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 36,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#track-transitions-and-eof-prevention",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "Track transitions and EOF prevention"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 13,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#startup-order-fifo",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "Startup order"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 24,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#snapserver-configuration-fifo",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "Snapserver configuration"
                        }
                      }
                    ]
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 8,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "#tcp-sink",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "TCP sink"
                  },
                  "unorderedListChildren": {
                    "$type": "pub.leaflet.blocks.unorderedList",
                    "children": [
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 9,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#tcp-layer-map",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "Layer map"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 15,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#pcm-sink-vtable-pcm-tcpc",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "PCM sink vtable"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 20,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#connection-lifecycle",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "Connection lifecycle"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 18,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#reconnect-on-error",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "Reconnect on error"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 13,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#startup-order-tcp",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "Startup order"
                        }
                      },
                      {
                        "$type": "pub.leaflet.blocks.unorderedList#listItem",
                        "content": {
                          "$type": "pub.leaflet.blocks.text",
                          "facets": [
                            {
                              "index": {
                                "byteEnd": 24,
                                "byteStart": 0
                              },
                              "features": [
                                {
                                  "uri": "#snapserver-configuration-tcp",
                                  "$type": "pub.leaflet.richtext.facet#link"
                                }
                              ]
                            }
                          ],
                          "plaintext": "Snapserver configuration"
                        }
                      }
                    ]
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 23,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "#auto-discovery-via-mdns",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Auto-discovery via mDNS"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 14,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "#ffi-boundary-cratessys",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 24,
                          "byteStart": 14
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          },
                          {
                            "uri": "#ffi-boundary-cratessys",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 25,
                          "byteStart": 24
                        },
                        "features": [
                          {
                            "uri": "#ffi-boundary-cratessys",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "FFI boundary (crates/sys)"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 22,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "#settings-and-startup-cratessettings",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 37,
                          "byteStart": 22
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          },
                          {
                            "uri": "#settings-and-startup-cratessettings",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 38,
                          "byteStart": 37
                        },
                        "features": [
                          {
                            "uri": "#settings-and-startup-cratessettings",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Settings and startup (crates/settings)"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 20,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "#other-pipe-consumers",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Other pipe consumers"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 24,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "#gotchas-and-known-limits",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Gotchas and known limits"
                  }
                }
              ],
              "startIndex": 1
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Overview"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 49,
                    "byteStart": 21
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "Both sinks write raw S16LE stereo PCM at 44100 Hz — the same byte stream snapserver expects regardless of source type. There is no Rust crate involved, both are pure-C PCM sinks with a thin Rust FFI wrapper for configuration."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Choosing FIFO vs TCP"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "| | FIFO sink | TCP sink |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "|---|---|---|"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 48,
                    "byteStart": 35
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| Filesystem entry required | Yes (/tmp/snapfifo) | No |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 34,
                    "byteStart": 27
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 43,
                    "byteStart": 37
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| Snapserver source type | pipe:// | tcp:// |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "| Startup order sensitive | Yes — rockboxd first | Yes — snapserver first |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "| Reconnect on snapserver restart | No (FIFO stays open) | Yes (auto on next play) |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 86,
                    "byteStart": 65
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| Auto-discovered in UI | No (static virtual device) | Yes (mDNS _snapcast._tcp.local.) |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 44,
                    "byteStart": 29
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| stdout pipe support | Yes (fifo_path = \"-\") | No |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 20,
                    "byteStart": 11
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 40,
                    "byteStart": 23
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 60,
                    "byteStart": 43
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| Config | fifo_path | snapcast_tcp_host + snapcast_tcp_port |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 8,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "Use FIFO when you want stdout piping or prefer the traditional pipe model."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 7,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "Use TCP when you want UI-based auto-discovery, multiple snapservers, or don't want a filesystem dependency."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "FIFO sink"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "FIFO layer map"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "plaintext",
              "plaintext": "\n┌────────────────────────────────────────────────────────┐\n│  Rockbox C firmware  (pcm.c, audio thread)             │\n│    pcm_play_data() → sink.ops.play()                   │\n│    pcm_play_dma_complete_callback() per chunk          │\n└───────────────────┬────────────────────────────────────┘\n                    │ raw S16LE stereo PCM chunks\n┌───────────────────▼────────────────────────────────────┐\n│  firmware/target/hosted/pcm-fifo.c                     │\n│    pcm_fifo_set_path()  — pre-creates FIFO, opens fd   │\n│    sink_dma_start()     — spawns fifo_thread           │\n│    fifo_thread()        — blocking write() loop        │\n│    sink_dma_stop()      — signals thread, keeps fd     │\n└───────────────────┬────────────────────────────────────┘\n                    │ blocking write() to FIFO or stdout\n┌───────────────────▼────────────────────────────────────┐\n│  Named FIFO  (/tmp/snapfifo)  or  stdout               │\n└───────────────────┬────────────────────────────────────┘\n                    │ read()\n┌───────────────────▼────────────────────────────────────┐\n│  snapserver  (pipe:// source)                          │\n│  — or —                                                │\n│  ffplay / aplay / custom consumer                      │\n└────────────────────────────────────────────────────────┘\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "facets": [
                {
                  "index": {
                    "byteEnd": 27,
                    "byteStart": 17
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "PCM sink vtable (pcm-fifo.c)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 33,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 60,
                    "byteStart": 45
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "firmware/target/hosted/pcm-fifo.c implements struct pcm_sink:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "| Op | Implementation |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "|-------------------|-------------------------------------------------------------|"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 27,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| init | pthread_mutex_init (recursive) |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 10,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| postinit | no-op |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 10,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| set_freq | no-op (output is always 44100 Hz; snapserver must match) |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 15,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 43,
                    "byteStart": 18
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| lock / unlock | pthread_mutex_lock/unlock |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 23,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 66,
                    "byteStart": 55
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| play | sink_dma_start — opens fd if needed, spawns fifo_thread |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 22,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| stop | sink_dma_stop — signals thread, joins; keeps fd open |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 13,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 54,
                    "byteStart": 37
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 72,
                    "byteStart": 58
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "fifo_pcm_sink is registered at index PCM_SINK_FIFO = 1 in firmware/pcm.c."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "The DMA thread"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 26,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 105,
                    "byteStart": 94
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "sink_dma_start(addr, size) stores the initial PCM pointer/length under the mutex, then spawns fifo_thread. The thread mimics a hardware DMA interrupt"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "loop:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "plaintext",
              "plaintext": "\nwhile not stopped:\n\n    1. lock → grab (data, size) → clear pcm_data/pcm_size → unlock\n\n    2. while size > 0 and not stopped:\n\n           n = write(fifo_fd, data, size)\n\n           handle EINTR/EAGAIN (retry)\n\n           advance data pointer, decrement size\n\n    3. lock → pcm_play_dma_complete_callback(OK, &pcm_data, &pcm_size) → unlock\n\n    4. if no more data: break\n\n    5. pcm_play_dma_status_callback(STARTED)\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Pacing comes naturally from the blocking FIFO write — the kernel suspends the"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "thread until the reader drains data, locking throughput to the consumer's rate."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "FIFO pre-open strategy"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 23,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "pcm_fifo_set_path(path) is called once at startup:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 4,
              "plaintext": "1. Create the FIFO"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "c",
              "plaintext": "\nmkfifo(path, 0666);   // EEXIST is ignored\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 4,
              "plaintext": "2. Open with a permanent writer reference"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "c",
              "plaintext": "\nfd = open(path, O_RDWR | O_NONBLOCK);\n\n// then clear O_NONBLOCK:\n\nfcntl(fd, F_SETFL, flags & ~O_NONBLOCK);\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 4,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 10,
                    "byteStart": 4
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    },
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 11,
                    "byteStart": 10
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 28,
                    "byteStart": 20
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 70,
                    "byteStart": 63
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Why O_RDWR? Opening O_WRONLY blocks until a reader is present. O_RDWR succeeds immediately and keeps the open-writer-count at ≥1 for the process lifetime — snapserver never sees premature EOF between tracks."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 10,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 20,
                    "byteStart": 10
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    },
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 21,
                    "byteStart": 20
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 123,
                    "byteStart": 113
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 149,
                    "byteStart": 142
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Why clear O_NONBLOCK? Writes must block when the kernel buffer is full to provide natural back-pressure. Leaving O_NONBLOCK set would produce EAGAIN and corrupt the stream."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "stdout mode"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 20,
                    "byteStart": 5
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "When fifo_path = \"-\", the sink writes to stdout:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "sh",
              "plaintext": "\nrockboxd | ffplay -f s16le -ar 44100 -ac 2 -\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 22,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 94,
                    "byteStart": 86
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "pcm_fifo_set_path(\"-\") redirects fd 1 to stderr before any PCM is written so internal printf() output never pollutes the PCM stream."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Track transitions and EOF prevention"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 15,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 24,
                    "byteStart": 21
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 38,
                    "byteStart": 31
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 142,
                    "byteStart": 135
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "sink_dma_stop() does not close fifo_fd. On POSIX, a named FIFO's read end sees EOF only when all write-side fds are closed. By keeping fifo_fd open across track boundaries, snapserver sees a continuous stream with no gaps."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Startup order (FIFO)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 38,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "rockboxd must start before snapserver."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "plaintext",
              "plaintext": "\n1. rockboxd starts  → pcm_fifo_set_path() → FIFO created, O_RDWR fd held\n\n2. snapserver starts → opens FIFO O_RDONLY → blocks until data flows\n\n3. Playback begins  → fifo_thread writes → snapserver distributes to clients\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Snapserver configuration (FIFO)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "ini",
              "plaintext": "\n# /etc/snapserver.conf  (or /usr/local/etc/snapserver.conf on macOS)\n\n[stream]\n\nsource = pipe:///tmp/snapfifo?name=default&sampleformat=44100:16:2\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.blockquote",
              "facets": [
                {
                  "index": {
                    "byteEnd": 47,
                    "byteStart": 45
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "On macOS, snapserver ≥ v0.35.0 ignores the -s CLI flag. Use the config"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.blockquote",
              "plaintext": "file."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "TCP sink"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "TCP layer map"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "plaintext",
              "plaintext": "\n┌────────────────────────────────────────────────────────┐\n│  Rockbox C firmware  (pcm.c, audio thread)             │\n│    pcm_play_data() → sink.ops.play()                   │\n│    pcm_play_dma_complete_callback() per chunk          │\n└───────────────────┬────────────────────────────────────┘\n                    │ raw S16LE stereo PCM chunks\n┌───────────────────▼──────────────────────────────────────┐\n│  firmware/target/hosted/pcm-tcp.c                        │\n│    pcm_tcp_set_host() / pcm_tcp_set_port()               │\n│    sink_dma_start()  — connects if needed, spawns thread │\n│    tcp_thread()      — blocking write() loop             │\n│    sink_dma_stop()   — signals thread, keeps socket      │\n└───────────────────┬──────────────────────────────────────┘\n                    │ blocking write() over TCP\n┌───────────────────▼────────────────────────────────────┐\n│  TCP socket  (snapserver host:port)                    │\n└───────────────────┬────────────────────────────────────┘\n                    │ recv()\n┌───────────────────▼────────────────────────────────────┐\n│  snapserver  (tcp:// source, server mode)              │\n│      │                                                 │\n│  ┌───┴──────┬──────────┐                               │\n│  ▼          ▼          ▼                               │\n│ snapclient snapclient snapclient                       │\n└────────────────────────────────────────────────────────┘\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "facets": [
                {
                  "index": {
                    "byteEnd": 26,
                    "byteStart": 17
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "PCM sink vtable (pcm-tcp.c)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 32,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 59,
                    "byteStart": 44
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "firmware/target/hosted/pcm-tcp.c implements struct pcm_sink:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "| Op | Implementation |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "|-------------------|-------------------------------------------------------------|"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 27,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| init | pthread_mutex_init (recursive) |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 10,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| postinit | no-op |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 10,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| set_freq | no-op (output is always 44100 Hz; snapserver must match) |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 15,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 43,
                    "byteStart": 18
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| lock / unlock | pthread_mutex_lock/unlock |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 23,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 65,
                    "byteStart": 55
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| play | sink_dma_start — connects if needed, spawns tcp_thread |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 22,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| stop | sink_dma_stop — signals thread, joins; keeps socket open |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 12,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 61,
                    "byteStart": 36
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "tcp_pcm_sink is registered at index PCM_SINK_SNAPCAST_TCP = 6 in"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 14,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "firmware/pcm.c."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Connection lifecycle"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 16,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 41,
                    "byteStart": 23
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 55,
                    "byteStart": 45
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "sink_dma_start() calls tcp_connect_once() if tcp_fd < 0:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "c",
              "plaintext": "\nstatic int tcp_connect_once(void)\n\n{\n\n    // getaddrinfo(tcp_host, port) → socket() → connect()\n\n    // returns fd on success, -1 on failure (logs error, drops audio)\n\n}\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 37,
                    "byteStart": 31
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 48,
                    "byteStart": 42
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "The socket is kept open across stop() → play() transitions, just as the FIFO fd is. snapserver's reader sees a continuous stream between tracks."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Reconnect on error"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 10,
                    "byteStart": 3
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 38,
                    "byteStart": 33
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 50,
                    "byteStart": 40
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 70,
                    "byteStart": 59
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 100,
                    "byteStart": 89
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 126,
                    "byteStart": 111
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 160,
                    "byteStart": 144
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 177,
                    "byteStart": 167
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 208,
                    "byteStart": 199
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "If write() returns a hard error (EPIPE, ECONNRESET, etc.), tcp_thread closes the socket (tcp_fd = -1) and sets tcp_stop = true. The next call tosink_dma_start() finds tcp_fd < 0 and attempts a fresh connect(). This handles snapserver restarts gracefully — the connection is re-established automatically on the next track or resume."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Startup order (TCP)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 64,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "snapserver must be running and listening before playback starts."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "plaintext",
              "plaintext": "\n1. snapserver starts  → listens on tcp://0.0.0.0:4953\n\n2. rockboxd starts    → pcm_tcp_set_host/port() stores config\n\n3. Playback begins    → sink_dma_start() → tcp_connect_once() → connects\n\n4. tcp_thread writes  → snapserver receives → distributes to clients\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 110,
                    "byteStart": 104
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Unlike the FIFO sink there is no permanent pre-connection at startup. The socket is opened on the first play() call."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Snapserver configuration (TCP)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "ini",
              "plaintext": "\n# /etc/snapserver.conf  (or /usr/local/etc/snapserver.conf on macOS)\n\n[stream]\n\nsource = tcp://0.0.0.0:4953?name=default&sampleformat=44100:16:2\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 13,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "settings.toml (manual config, not needed when selecting from the UI):"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "toml",
              "plaintext": "\naudio_output      = \"snapcast_tcp\"\n\nsnapcast_tcp_host = \"192.168.1.x\"   # IP of the machine running snapserver\n\nsnapcast_tcp_port = 4953            # default snapserver TCP source port\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Auto-discovery via mDNS"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 62,
                    "byteStart": 41
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "snapserver advertises itself via mDNS as _snapcast._tcp.local.. rockboxd"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 61,
                    "byteStart": 38
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "scans for this service at startup via scan_snapcast_servers() in"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 25,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 62,
                    "byteStart": 41
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "crates/server/src/scan.rs, which browses _snapcast._tcp.local. using the"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 7,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "mdns-sd crate and adds discovered servers to the shared devices list."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Discovered servers appear immediately in:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 6,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Web UI — the device picker in the control bar (lime-green radio icon)"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 18,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Desktop app (GPUI) — the device picker popup"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 59,
                    "byteStart": 35
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Clicking a discovered server calls PUT /devices/:id/connect, which:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.orderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 33,
                          "byteStart": 6
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 67,
                          "byteStart": 38
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Calls pcm_tcp_set_host(device.ip) and pcm_tcp_set_port(device.port)."
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 44,
                          "byteStart": 6
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Calls pcm_switch_sink(PCM_SINK_SNAPCAST_TCP)."
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.orderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 38,
                          "byteStart": 9
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 57,
                          "byteStart": 40
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Persists audio_output = \"snapcast_tcp\", snapcast_tcp_host, and"
                  }
                }
              ],
              "startIndex": 1
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 23,
                    "byteStart": 10
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "No manual settings.toml editing is needed when using the UI."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "facets": [
                {
                  "index": {
                    "byteEnd": 24,
                    "byteStart": 14
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "FFI boundary (crates/sys)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "FIFO"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "rust",
              "plaintext": "\n// crates/sys/src/lib.rs\n\nextern \"C\" { fn pcm_fifo_set_path(path: *const c_char); }\n\n\n// crates/sys/src/sound/pcm.rs\n\npub fn fifo_set_path(path: &str) {\n\n    let cpath = CString::new(path).expect(\"path must not contain null bytes\");\n\n    unsafe { crate::pcm_fifo_set_path(cpath.as_ptr()) }\n\n    std::mem::forget(cpath);  // C code stores and re-reads pointer at runtime\n\n}\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "TCP"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "rust",
              "plaintext": "\n// crates/sys/src/lib.rs\n\nextern \"C\" {\n\n    fn pcm_tcp_set_host(host: *const c_char);\n\n    fn pcm_tcp_set_port(port: c_ushort);\n\n}\n\n\n// crates/sys/src/sound/pcm.rs\n\npub fn tcp_set_host(host: &str) {\n\n    let chost = CString::new(host).expect(\"host must not contain null bytes\");\n\n    unsafe { crate::pcm_tcp_set_host(chost.as_ptr()) }\n\n    std::mem::forget(chost);\n\n}\n\n\npub fn tcp_set_port(port: u16) {\n\n    unsafe { crate::pcm_tcp_set_port(port) }\n\n}\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 16,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 118,
                    "byteStart": 104
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "std::mem::forget is used in both cases because the C code stores the raw pointer and reads it later (in sink_dma_start's connect / fallback path)."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 20,
                    "byteStart": 13
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Dropping the CString would free the memory while C holds a dangling pointer."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Since these are startup-time config calls, leaking is acceptable."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "facets": [
                {
                  "index": {
                    "byteEnd": 37,
                    "byteStart": 22
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Settings and startup (crates/settings)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 42,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "crates/settings/src/lib.rs:load_settings() handles both sinks:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "rust",
              "plaintext": "\nSome(\"fifo\") => {\n\n    let path = settings.fifo_path.as_deref().unwrap_or(\"/tmp/rockbox.fifo\");\n\n    pcm::fifo_set_path(path);\n\n    pcm::switch_sink(pcm::PCM_SINK_FIFO);\n\n}\n\nSome(\"snapcast_tcp\") => {\n\n    if let Some(ref host) = settings.snapcast_tcp_host {\n\n        let port = settings.snapcast_tcp_port.unwrap_or(4953);\n\n        pcm::tcp_set_host(host);\n\n        pcm::tcp_set_port(port);\n\n        pcm::switch_sink(pcm::PCM_SINK_SNAPCAST_TCP);\n\n    }\n\n}\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "All Snapcast settings keys"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "| Key | Type | Default | Sink | Description |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "|----------------------|--------|-----------------------|-------|------------------------------------------|"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 14,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 35,
                    "byteStart": 26
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 51,
                    "byteStart": 45
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 69,
                    "byteStart": 55
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| audio_output | string | \"builtin\" | both | \"fifo\" or \"snapcast_tcp\" |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 11,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 42,
                    "byteStart": 23
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 69,
                    "byteStart": 66
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| fifo_path | string | \"/tmp/rockbox.fifo\" | FIFO | FIFO path, or \"-\" for stdout |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 19,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| snapcast_tcp_host | string | — | TCP | IP / hostname of the snapserver machine |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 19,
                    "byteStart": 2
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 32,
                    "byteStart": 28
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "| snapcast_tcp_port | u16 | 4953 | TCP | snapserver TCP source port |"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Other pipe consumers"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Since both sinks carry raw S16LE stereo 44100 Hz PCM, the FIFO sink works with any tool that accepts that format:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "sh",
              "plaintext": "\n# Play directly with ffplay (stdout mode)\n\nrockboxd | ffplay -f s16le -ar 44100 -ac 2 -\n\n\n# Encode on the fly\n\nrockboxd | ffmpeg -f s16le -ar 44100 -ac 2 -i - output.mp3\n\n\n# Play with sox\n\nrockboxd | play -t raw -r 44100 -e signed -b 16 -c 2 -\n\n\n# Inspect levels with aplay (Linux)\n\nrockboxd | aplay -f S16_LE -r 44100 -c 2\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 36,
                    "byteStart": 21
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "All of these require fifo_path = \"-\" and are only available with the FIFO sink. The TCP sink does not support stdout mode."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Gotchas and known limits"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "1. Startup order is critical for both sinks"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 4,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      }
                    ],
                    "plaintext": "FIFO: rockboxd must open the FIFO before snapserver. Reverse order causes"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 3,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      }
                    ],
                    "plaintext": "TCP: snapserver must be listening before playback starts. If snapserver"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "2. Fixed 44100 Hz, S16LE stereo"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 32,
                    "byteStart": 24
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Neither sink resamples. set_freq is a no-op. The firmware resamples tracks internally before they reach the sink, but the output is always 44100 Hz."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 33,
                    "byteStart": 10
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Configure sampleformat=44100:16:2 on the snapserver side."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "3. No volume control through the sink"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Volume is applied by the Rockbox DSP pipeline before PCM reaches the sink."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Adjust volume through the Rockbox API or client applications."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "4. Consumer back-pressure controls playback speed"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 31,
                    "byteStart": 24
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Both sinks use blocking write(). A slow or stalled consumer stalls"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 7,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "write(), which stalls the DMA callback loop, which pauses decoding. This is correct for synchronized output but means a crashed consumer freezes"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "playback. Restart snapserver to recover."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "facets": [
                {
                  "index": {
                    "byteEnd": 24,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "5. macOS snapserver.conf vs CLI flag"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 4
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "The -s flag to snapserver is silently ignored on macOS (≥ v0.35.0)."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 43,
                    "byteStart": 36
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 54,
                    "byteStart": 48
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Always use the config file for both pipe:// and tcp:// sources."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "6. TCP reconnect drops in-flight buffer"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 33,
                    "byteStart": 28
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "When the write loop detects EPIPE it closes the socket immediately. The current audio buffer is discarded. Reconnection happens on the next"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 16,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "sink_dma_start() call, so there will be a brief audio gap when snapserver restarts."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "facets": [
                {
                  "index": {
                    "byteEnd": 23,
                    "byteStart": 16
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 39,
                    "byteStart": 31
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "7. Logging uses tracing, never println!"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 55,
                    "byteStart": 48
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 65,
                    "byteStart": 57
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 79,
                    "byteStart": 70
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 189,
                    "byteStart": 166
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "All Rust-side diagnostic output must go through tracing. println! and eprintln! bypass the log filter and — in stdout/FIFO mode — can corrupt the PCM stream. Use RUST_LOG=debug rockboxd to see debug output on stderr.\n\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 40,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 72,
                    "byteStart": 40
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    },
                    {
                      "uri": "https://github.com/tsirysndr/rockbox-zig",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 73,
                    "byteStart": 72
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "The full implementation is available at github.com/tsirysndr/rockbox-zig."
            }
          }
        ]
      }
    ]
  },
  "bskyPostRef": {
    "cid": "bafyreielexybiprmjp22f3ofkgy7xpikauw5tx3z4ev6fovs6dxuf2bea4",
    "uri": "at://did:plc:7vdlgi2bflelz7mmuxoqjfcr/app.bsky.feed.post/3mkjx5pisuk2m",
    "commit": {
      "cid": "bafyreia4troau77hmsijgoq4pidoyyqthfhy7q4jvgybnjxeectojtvsfu",
      "rev": "3mkjx5pkf5o2w"
    },
    "validationStatus": "valid"
  },
  "description": "",
  "publishedAt": "2026-04-28T06:24:02.128Z"
}