Riff

Technical questions

Why does Riff use FlatBuffers, run in two processes and look so plain? This is a more technical FAQ about choices shaped by inexpensive Android head units.

The move of user settings and history to FlatBuffers belongs to 1.1.0 RC. The public 1.0.2 build in the download section still uses SQLite/WAL for those records.

Why FlatBuffers when Android has Room and SQLite?

Room and SQLite are normal, reasonable choices. Riff also used a SQLite database with WAL. But a head unit gets its power from the car, and its storage controller and firmware may not behave as an application expects.

SQLite's durability, including WAL with synchronous=FULL, depends on storage honoring sync requests. If a controller reports a write as persistent too early or breaks write ordering, sudden power loss can damage data. SQLite documents these storage limitations.

In 1.1.0, Riff stores state as immutable FlatBuffers snapshots. It prepares and validates a new version before switching the current pointer, retaining the previous accepted version for recovery. That gives the application explicit publication, validation and fallback rules. FlatBuffers itself cannot repair a faulty controller or guarantee survival of every power cut. Process death and injected write failures can be tested in software; the controller's behavior requires actual power cuts or a dedicated test rig. That hardware validation remains open for Riff.

Runtime overhead matters too. On an old mobile processor, temporary objects and garbage collection can interfere with playback. FlatBuffers supports accessing data directly in its binary buffer without requiring the whole structure to be unpacked. Adapters and UI code still allocate objects: the format does not eliminate allocations or GC.

Why two processes and so much communication between them?

Decoding and ML inference consume memory and CPU time. An older head unit has little to spare. Android may terminate a process under memory pressure; a native decoder crashing on a particular file is another risk. High CPU usage alone does not mean Android will necessarily kill a process. The system's process-lifetime rules are described in the Android documentation.

Riff therefore separates playback from library preparation. Player owns the interface and audio; Indexer reads files and runs analysis. Indexer death is an expected recovery case: it must be able to restart while Player retains its accepted library and current queue.

That leads to file descriptors, acknowledgments, retries and session numbers. Riff needs to distinguish a lost reply from an unexecuted command, a retry from a new action, and an old process from its replacement. In 1.1.0, acceptance into memory is also distinct from durable storage. The Player and Indexer article explains the communication in more detail.

Why does the interface look so basic? What about Material Design?

The author knows Material Design and Compose. Riff's interface is built with Compose, but it targets displays that may have low contrast, sunlight glare and imprecise touch input.

A large, high-contrast button remains visible and provides a generous touch target on such a panel. Small controls, thin labels and subtle gradients can become hard to distinguish in the same conditions. Riff therefore uses open space, large touch areas and clear separation between foreground and background. Its appearance is deliberately utilitarian: reading and hitting the right control take priority.

Large buttons do not remove restrictions on actions while driving. Those depend on the application mode and the vehicle system.

What is the screen with the bank card for?

The card is a physical ruler. A standard bank card has known dimensions, so matching it to the on-screen outline provides a measurement of the display's actual scale.

Android converts dp to pixels using the density reported by the device. If the same firmware reports 160 dpi on panels with different physical densities, identical interface elements end up at different physical sizes. Using dp alone cannot correct that input error. How Android handles display density.

Calibration gives Riff a measurement it can use to choose an appropriate scale. If the controls already feel comfortably sized on your device, calibration is optional. It is mainly useful when the firmware's display-density information cannot be trusted.

Could the AI selection be better?

The model can improve: it can describe music more accurately, estimate confidence better and make fewer mistakes. But recognizing musical properties is only part of choosing the next track.

An offline head unit has no listening history from millions of people or evidence about which recordings they tend to play together. Even recognizing EDM perfectly does not tell it which particular track you want now. Always choosing the closest match can also keep returning to the same corner of a large library.

Riff therefore combines several layers: the model describes the music, your ratings influence preferences, history reduces the probability of recent repeats, and weighted random selection gives other suitable tracks a chance. A better model can help, but variety still needs its own mechanism. Randomness does not correct classification errors; it helps prevent selection from endlessly repeating the closest candidates.

The current model remains a research-derived beta model. The probabilistic selection article explains how model outputs, ratings and history become a queue.