◀ Back to contents

Preferences

This page covers the preference settings. For why the audio settings decide whether a measurement is trustworthy - bit-exact, exclusive capture and playback - see Theory of operation ▸ Audio backend.

Opened from Tools ▸ Preferences. Live capture and generator playback are paused while the dialog is open, so device or format changes don't tear a running stream; OK applies and saves, Cancel reverts. The window resizes: it opens at its minimum size, and widening it widens the entry fields in place rather than leaving white space. The dialog is organised into five tabs, in this order: Audio, Look & Feel, Oscilloscope, FFT and Frequency response. Audio is the first tab and the one the dialog opens on - it is where a measurement session starts.

Audio

Preferences - Audio tab

Selects the sound back end and the input / output devices and formats - the settings that decide whether a measurement is bit-exact (see Theory ▸ Audio backend).

SettingNotes
Backend Web Audio (the default - the browser's own capture and playback path, see Phonalyser in a browser) or QA40x - a QuantAsylum QA402 / QA403 analyzer driven directly over WebUSB (see Theory ▸ QA40x). The QA40x row is listed even in a browser that cannot use it, disabled and carrying its reason - Requires WebUSB over https:// or localhost - works in Chrome; other Chromium browsers may not expose it - because that is something you can act on. Changing the selection re-enumerates the device lists. Once you are connected to a Phonalyser server the same list also carries that bench's backends, written <server> -> <backend>; a backend the bench reports as unavailable or not operational - CoreAudio on a machine that is not a Mac, a QA40X with no analyzer plugged into it - is not listed at all, because it is not a choice you have.
Input - Device / Sample rate The capture (ADC) device and the rate it runs at. There is no bit-depth setting for Web Audio: the browser hands the page float32 samples downstream of the system mixer, so there is no depth to choose and none to report. The rate is likewise the one Windows has configured for that device rather than one this dialog picks - see Windows input device. A QA40x does have a depth, and shows the combo.
Output - Device / Sample rate The playback (DAC) device and rate, used by the generator and the frequency-response sweep. Same rule: float32 out through the browser, with a bit depth offered only for the QA40x.
Web Audio does not take a device exclusively - the browser goes through the system mixer, so other applications keep playing and recording alongside it. That is convenient and it is also the reason a browser capture is never bit-exact: whatever the mixer does to the stream is already done by the time the page sees it. The QA40x is exclusive: WebUSB claims the analyzer's interface, so it must not be open in the QuantAsylum software or in another tab. If a device list is empty or a device will not open, grant the page microphone permission, close whatever holds the analyzer, and press Scan devices.
QA40x. The QA40x backend appears in the list only when a QA402 / QA403 is connected and the browser supports WebUSB. With it selected the input and output devices are the one analyzer, and its input and output sample rates are locked equal (one hardware clock) - set one and the other follows. Full-scale is the device's own range calibration, so manual calibration is not possible: the Calibrate dialog opens read-only and simply shows the card's built-in full-scale for each channel. The rate list follows the model that is actually attached: 48 / 96 / 192 kHz on both, plus 384 kHz on a QA403, whose sample-rate register has a fourth code the QA402 does not have. Capture and generator both run at whichever rate you pick - the two directions share that one clock at every rate.
Before the analyzer appears at all the browser has to have granted this page access to it, and the grant is raised by Scan devices - nothing else prompts, including selecting QA40x in the list. Until then a plugged-in QA403 lists as nothing. The full set of prerequisites - Google Chrome, https:// or localhost, and a WinUSB-class driver on Windows - is in Phonalyser in a browser ▸ the QA40x and what WebUSB needs.
The analyzer is handed back when nothing is using it. As soon as the last consumer detaches, it is parked at its safe ranges, stopped and released. The WebUSB claim is exclusive, so an idle but still claimed analyzer would lock the QuantAsylum software - and every other tab - out of it for as long as this page stayed open.
QA40x settings. With the QA40x selected, the QA40x preferences button appears next to the backend list - it opens the analyzer's own settings, which no other backend has. The dialog reads the instrument's identity and live telemetry straight off it (firmware version, serial number, USB voltage and current, ISO-supply current on the QA402, board temperature and the two capability words) and switches the front-panel I2S expansion port on or off. The port is started before a measurement session and stopped after it; its frame width comes from the output bit depth, so while I2S is on that list offers the port's 16 and 32 bits in place of the analyzer's 24 - the input stays at 24 throughout. Like every other page of the dialog these take effect only on OK. Switching the port is experimental: QuantAsylum documents the connector itself, but not the USB registers that enable it and set its frame width, so that control path is not vendor-confirmed - as the dialog itself states.

Preferences - Audio tab with the QA40x backend selected

QA40x settings dialog

The buttons on the backend row

Three buttons share the row with the backend list. Two are always present; the third appears only for a backend that has settings of its own.

ButtonWhat it does
Scan devices Reads the selected backend's device list again - the explicit "I don't trust this list" gesture. For Web Audio it re-runs the browser's own enumerateDevices, so a card plugged in or pulled out since the page loaded appears (or disappears) without reloading the page. It also re-composes the backend list itself, which is how an analyzer connected since start-up shows up as a backend at all, and it asks a connected server for its backends again. For the QA40x this click is the point: WebUSB opens its device chooser only from a real user gesture, so the automatic enumeration at page load can list only analyzers you have already granted - pressing Scan is what raises the chooser for a new one. A scan never disturbs a measurement: for a remote bench the device list is only read, never re-selected.
Servers... Opens the server list - the Phonalyser servers on the network, and the ones you added by address. It is there whenever the installation carries the network module, and it does not depend on the selected backend: a bench has to be chosen before any of its backends can be.
<backend> preferences The selected backend's own settings, and the button is named after that backend - QA40x preferences for a QA402 / QA403, whether the analyzer is on this machine or on a bench across the network. It is hidden for a backend that has no settings of its own, which is every other one at present.

Card - per-card calibration profiles

Full-scale calibration - the volts RMS that map to the converter's ±1.0 digital full-scale, and therefore the absolute dBV axis, the scope's voltage readouts and the generator's output level - belongs to the physical card, not to the back end or to the device name a driver happens to report. A card profile ties that calibration to the card itself and follows it across back ends, because the same card shows up under different names - the browser wraps it as Line (...) or Speakers (...), a bench may report the bare name or an ALSA [plughw:...] suffix. A profile keeps a single list of recognized device names - the names and name-fragments that identify this card - so one calibration serves it however the device is spelled.

Matching is against the name you see in the device list. The browser's own device identifiers are opaque strings that mean nothing to a human and change between sessions, so a profile is resolved against the device's displayed label instead - which means you can write a match pattern by reading the Device combo, exactly as on the desktop.

A profile records, in one place: the card's name; which directions it is calibrated for (Provides: input, output or both); whether it has one channel or two (Channels: Mono or Stereo); how its two channels track each other per direction (Coupling: Linked or Independent); its list of switchable ranges (attenuator / gain / DIP positions); and the measured full-scale of each range - always stored per channel, since no two physical channels are calibrated exactly alike.

All card profiles live in one document in the browser's local storage, under the key phonalyser.devices. It is seeded on first run with ready profiles for a few well-known interfaces (the E1DA Cosmos ADC, the JLsounds I2SoverUSB and the CUBILUX CB5) and everything you edit in this tab is written back to it.

Because it is browser storage and not a file, the store is per browser and per site: your calibrations do not follow you to another browser or another machine, and clearing this site's data deletes them. There is no devices.yaml and no user data directory in this build - that is the desktop application's arrangement.

The device store - reading and editing it by hand

The ordinary way to read or edit it is Tools ▸ Devices ▸ Current, which opens the live document in a JSON editor that knows the shape below and will not let an edit through without checking it. Everything this section describes is what you see in that window.

The store can also be reached with the browser's developer tools, which is the raw fallback - no completion, no checking, and a save takes effect only on the next load: open them with F12, go to Application ▸ Storage ▸ Local Storage, select this page's origin and look at the phonalyser.devices row. Use it when you want to copy the whole document somewhere, or to read a store the application will not start on.

The value is JSON: two version markers and one object per card. An annotated store, with every key the application reads:

{
  "formatVersion": 1,        // schema marker - changes only if the schema itself changes
  "contentVersion": 2,       // catalogue generation - drives the once-per-release upgrade merge (below)
  "audioDevices": [          // the list of card profiles
    {
      "name": "E1DA Cosmos ADC",         // logical profile name shown in the Card combo (required)
      "match": [ "E1DA Cosmos ADC" ],    // recognized device names: substrings (case-insensitive) that identify this card
      "input": {                         // a per-direction block - "output" is the same shape
        "channels": "INDEPENDENT",       // MONO = one channel; LINKED = two channels, shared range switch; INDEPENDENT = two channels, each its own
        "ranges": [                      // one row per front-end position (attenuator / gain / DIP)
          { "label": "1.7V", "fsVrms": { "left": 1.7, "right": 1.7 }, "calibrated": true },   // measured full-scale per channel; calibrated = you measured this row (upgrades never overwrite it)
          { "label": "3.5V", "fsVrms": { "left": 3.5, "right": 3.5 } }                        // a range you have not yet calibrated - still the seeded nominal
        ],
        "activeRange": { "left": "1.7V", "right": "3.5V" }   // INDEPENDENT: a { left, right } map, each channel's own active row
      }
    },
    {
      "name": "I2SoverUSB",
      "match": [ "I2SoverUSB", "JLsounds" ],
      "output": {
        "channels": "LINKED",
        "ranges": [ { "label": "default", "fsVrms": { "left": 2, "right": 2 } } ],
        "activeRange": "default"         // LINKED / MONO: a single label (both channels switch together)
      }
    }
  ],
  "bindings": {                          // device -> card choices you made in the dialog; omitted entirely while nothing is bound
    "Line (E1DA Cosmos ADC)": "E1DA Cosmos ADC"   // this exact device name always resolves to this card, whatever "match" would say
  }
}

The // comments above are explanation, not part of the document - JSON has no comments, so a value you paste back must not contain them.

What the keys mean:

Editing it in the developer tools has one rule the built-in editor does not: do it with the application's tab closed, then reopen the page. The store is read once at start-up and rewritten whenever the app saves, so an edit typed into storage while the app is running is simply overwritten. The JSON editor has no such problem - it hands the document to the running application itself, which is why its changes take effect at once. Entries with no name (or ranges with no label) and unrecognised keys are skipped with a warning in the console rather than aborting the load, so a small mistake costs one entry, not the whole store. Deleting the phonalyser.devices key makes the next load re-seed the built-in defaults, which loses any cards you added or calibrated.

A stored document that exists but cannot be parsed at all is never silently replaced by defaults. In the browser these are entries in the page's local storage, not files on disk, so the quarantine is a copy under a new key rather than a file beside another: the unreadable value is copied to <key>.corrupt and the failure is logged, so what survives in it can still be recovered - read it out with the browser's developer tools - and the next save cannot overwrite the evidence. This covers the settings document and the device store alike. Local storage is per browser and per origin: clearing site data for this page discards both, and they do not follow you to another browser or another machine.

Upgrades never lose your work. The seeded catalogue carries a contentVersion; when a new release ships a higher number the merge runs once, then records the new number so it never repeats. For each catalogue card it finds your matching card - by name or by a shared recognized-name entry, so a card you renamed is still found - and reconciles only its range table: a range the release added appears in your store, and a range you both have is refreshed to the release's nominal full-scale - except a range you calibrated, which keeps its measured value and its calibrated flag. A card that is entirely new in the release is added whole. Everything else is yours and is never touched: your calibrated values, the cards and ranges you added by hand, your recognized-name list, your channel modes and your active-range selections all survive.

Each direction - Input and Output - carries its own Card combo above its device controls. Bind a card profile when you want any of:

A plain sound card with a fixed front-end needs no profile - leave its Card selection empty and it behaves exactly as before this feature existed, drawing on the single shared full-scale that predates card profiles.

The Card combo and the edit button

The combo lists every card profile that has at least one range in this direction - the pre-seeded known cards included - plus New card...; a device with no card shows an empty selection. A small pencil (edit) button beside the combo opens the card editor for the selected card.

The combo re-resolves automatically whenever the device changes: the app matches the new device name against every card's recognized-name list and, when one correlates, selects it and shows its ranges. When more than one could match, the most specific (longest matching) recognized name wins. Selecting the combo entry and pressing OK is what binds the device, and it does two things at once: it records the device->card choice in the store's bindings map - which is what resolves the device from then on, ahead of any name matching - and it appends the device's exact name to that card's recognized-name list as a visible alias. A device is never bound to two cards at once, and it is the bindings map that guarantees it: there is exactly one entry per device, so re-binding to another card replaces that entry. The alias left behind in the previous card's list is not removed - but it no longer decides anything, and the device follows the new card.

When no card matches the device, the app does not bind one silently: it offers to make one - Create a new card for '<device>'? - and on yes opens the card editor pre-filled from the device name. You can also pick New card... yourself, or leave the selection empty to keep the device unbound.

The application never binds a card silently. Two identical interfaces report identical names and are indistinguishable to software - only you know which physical box is plugged into which signal chain, so an uncertain match is always offered for confirmation rather than applied on its own.

Leaving the selection empty keeps the device unbound: it falls back to the single shared full-scale that predates card profiles, and the range table stays hidden. That global calibration survives only for the current session and is never written back to the settings document - a document from before 1.2 is still read once, and the entries disappear with the first save. The cards in the device store are the persistent home of every full-scale value, so bind every measurement device to a card. Selecting New card... creates a profile whose first range is seeded from the calibration currently in effect, so the new card starts already calibrated to wherever you were.

The card editor

The pencil button opens the card editor, a small dialog holding every property of a card except its per-range voltages (those come only from the Calibrate flows).

Card editor dialog

PropertyNotes
Card name The logical name shown in the Card combo. Must be non-empty and unique.
Provides Input only, Output only or Both - which directions this card is calibrated for. A card appears only in the combos for the directions it provides. Narrowing a card that already carries calibrated ranges in a dropped direction asks first, since those ranges would be deleted.
Channels Mono (one physical channel - a single full-scale serves both sides) or Stereo (two channels).
Recognized device names One device name - or a fragment of one - per line. This card is chosen whenever any line appears (as a substring, case-insensitive) in the device name the driver reports; when several cards could match, the most specific line wins. The list holds the card's recognition patterns plus the visible aliases appended when you bind a device to it. The binding itself does not live here: it is an entry in the store's bindings map, and it wins over any matching this list produces.
Input / Output - Coupling Per direction, how a Stereo card's two channels track each other. Linked switches both channels' ranges together, but each channel still keeps its own calibrated full-scale value - coupling shares only which range is selected, never the measured volts. Independent lets each channel select its own active range. A Mono card ignores this.

The Input and Output groups grey out to match Provides - an input-only card shows its Output group disabled, and vice-versa.

Ranges table

A range is one setting of the card's analog front-end - an attenuator step, an input-gain position, a DIP or relay setting - each with its own full-scale voltage. The table lists one row per range:

ColumnNotes
Active marker Selects the one range whose full-scale is currently in force. Exactly one range is active at a time; switch it to match the front-end position you have set on the hardware.
Label An editable name for the range - e.g. the attenuator or gain position it stands for (0 dB, −20 dB, a DIP code).
Add / Remove Add a range for another front-end position, or remove one.

The row carries no full-scale voltage field. A range's full-scale value is not typed here - it is set exclusively by running the crosshair Calibrate flow with that range marked active, which writes the measured full-scale into it.

A plain card that never changes its front-end keeps a single row. A switchable interface gets one row per position, and you flip the active marker whenever you change the hardware.

A range switch takes effect at once, on that channel alone. Marking a range active applies its full-scale to the channel you marked it on immediately, rather than waiting for OK to write both channels together. So on an Independent card you can sit the two channels on different ranges and watch each one's readout follow its own switch as you go.

How many active columns the table shows follows the card's Channels and per-direction Coupling, both set in the card editor:

A QA40x input range is not the dBV it is called

For a QA402 / QA403 - on this machine or on a bench across the network - every input row carries a second label after the plain one:

0 "dBV" real 0 dBFS or -9 dBV

It is there because the analyzer's range names are not RMS voltages, and reading them as such is off by about 9 dB. A QA "N dBV" input range is an N dBFS reference measured peak-to-peak across the differential pair; the RMS full-scale voltage that corresponds to it sits roughly 9 dB lower - a factor of 2 for peak-to-peak versus peak, and √2 for peak versus RMS, is 2·√2 ≈ 9.03 dB. So the range the analyzer calls 0 dBV is a full scale of about −9 dBV RMS, and that is the number every volt, dBV and derived figure in this application is computed from.

Three things follow. The second label is display only: the stored card keys, the range radios and the values written into the device store keep the plain N dBV, so nothing in your store changes because of it. The output ranges are left alone - those are genuine dBV, with no hidden peak-versus-RMS term. And a loopback measurement of a QA40x reads its own generator about √2 (3 dB) louder than the level asked for, which is not a fault in either direction but the balanced output driving a differential input; see Theory ▸ The QA40x analyzer backend.

Worked example - two cards on one bench

A typical bench has an E1DA Cosmos ADC on the input, whose DIP attenuator switches its full-scale, and a separate JLsounds card in use as well. Bind each once and their calibrations never cross:

  1. Select the Cosmos in the input Device combo. Its Card combo pre-selects the known card E1DA Cosmos ADC - a ready profile seeded into the device store - with a nominal range for every DIP position, 1.7V up to 43V, already in place. Confirm it by selecting it and pressing OK. (For an unrecognised card you would instead choose New card... and add the rows by hand.)
  2. Because the Cosmos DIPs move per channel, its table shows a Left and a Right active column. In each column mark the range that matches that channel's DIP position - the two columns track the two switches independently.
  3. Calibrate those ranges through the crosshair Calibrate flow - on the scope only the row for the currently measured channel is live (the other row is disabled and stays empty), writing that channel's measured full-scale into its active range; measure the other channel to calibrate its row in turn (the FFT's own Calibrate likewise writes only the channel being analyzed). Then flip a DIP, switch that channel's active column to the other row, and calibrate again; the new measurement lands in that channel's second range.
  4. Later, select the JLsounds device: it carries its own card and its own calibration, untouched by the Cosmos.

Because flipping the DIP means nothing more than switching which range is active, and the values follow the card rather than the back end, switching between the two cards - or reopening the Cosmos under a different back end once its name is bound there - never mixes their full-scales up.

How calibration writes into a card

The crosshair Calibrate flows anchor the scale against a known reference - the scope and FFT ADC calibration for the input, and the generator DAC calibration for the output. Each writes the measured full-scale into the active range of the card the device resolves to. If the selected device has no profile yet, the first calibration creates one automatically - logical name from the device, a recognized-name entry for it, and one seeded range - so you get a card profile simply by calibrating, without visiting this tab first.

Calibration is always per channel. A Stereo card opens the shared two-row dialog with a Left and a Right field, because no two physical channels are calibrated exactly alike - coupling shares only which range is selected, never the measured values; a Mono card shows a single row. On the scope ADC path only the row for the currently measured channel is live - the other row is disabled and stays empty, so you calibrate one channel at a time; on the FFT ADC path only the row for the channel being analyzed is enabled, since the FFT works one channel at a time; the DAC path drives both rows. Each row writes only its own channel's active range.

When the values take effect

The card's active-range full-scale is resolved - by the bindings entry for the device if you made one, otherwise by the match rule - and applied at these moments: when capture starts (input), when the generator starts (output), and when you close this dialog with OK. From that point the resolved value drives the FFT's absolute dBV axis, the oscilloscope's voltage readouts and the generator's output scaling.

Two cases fall back to the single shared full-scale that predates card profiles: a device that resolves to no card at all, and a card whose active range is still uncalibrated - a row holding zero is an empty row, not a measurement, so it contributes no calibration of its own. That shared value is runtime state only: it is no longer written to the settings document (a document from before 1.2 is still read once, and the entries disappear with the first save), because saving it would store the last calibrated card you selected as the machine-wide default and hand it to the next uncalibrated device as though it had been measured there. The persistent home of every full-scale value is the device store - the cards.

Because an uncalibrated selection measures on a nominal placeholder rather than on your hardware, closing this dialog with OK after changing the backend or either device warns you once when the committed selection has no calibration behind it, naming the input, the output or both. A device that reports its own full-scale (calibrationFromDevice, a QA40x-class instrument) is exempt - there is nothing for you to calibrate.

A value also takes effect immediately when you close the calibration dialog with the Calibrate button: the new full-scale is written into the device store and applied through the live card binding to every consumer - the oscilloscope's scaling, the FFT's dBV axis and the generator's amplitude ceiling - without a restart.

Every card resolves each channel to its own calibrated full-scale, so every scope trace and measurement uses its own channel's value and the FFT's absolute dBV axis follows the analyzed channel. An Independent card can additionally sit the two channels on different ranges; a Linked card keeps both on the same range but still uses each channel's own measured value.

Measuring through a Phonalyser server

The hardware does not have to be wired to the machine you are sitting at. The desktop Phonalyser running as a server on the machine that is wired to the converters turns it into a bench, and this application then measures through it: you pick one of the bench's backends in the list above and everything - devices, formats, cards, calibration, the generator, the sweeps - works as though the hardware were plugged in here. For what happens on the wire, see Theory ▸ A bench across the network.

There is deliberately no authentication. A server is a bench instrument for a network you trust - the room your measurement equipment is in - not something to expose to the open internet.

The server list

The Servers... button on the backend row opens Phonalyser servers. The list starts empty, and one address fills it. A browser cannot listen for the announcements servers broadcast on the local network - a page has no way to receive them - so nothing arrives by itself the way it does on the desktop. Instead you type the address of one server in the row at the bottom of the window, and it reports every other server it has heard: its peer table is the whole of discovery here. One reachable bench, and the room appears.

The Phonalyser servers list

The table has three columns:

ColumnNotes
Server The name the server calls itself - its --name, or the machine's own name. A server you typed in by address carries the name it reported when it was added, not the address you typed.
Address Host and port, e.g. bench-pc:8377.
State live - answering right now; offline - a server this installation remembers that is not answering; connected - the one this application is measuring through. Liveness is asked, not announced: opening the window puts the question to every remembered server at once, and it is repeated while the window stays open - so a bench that is switched off turns from live to offline on its own rather than at your next click. That polling lives and dies with this window; nothing probes your network in the background.

Three buttons sit under the table. Connect and Remove act on the selected row; Disconnect does not - there is only ever one session, and that is the one it ends.

ButtonWhat it does
Connect Opens the session and reads the server's backends, which then join the backend list. Double-clicking a row - or selecting it and pressing Enter - does exactly the same: it is the same path, refusals included. Greyed for the server that is already connected, so no route re-opens a session over a live one. A server that will not answer says so: No Phonalyser server answered at <address>, with the connection's own words after it.
Disconnect Ends the session - the live one, whichever row happens to be selected and even with no row selected at all; it is available whenever a server is connected and greyed when none is. Not the no-op it looks like: the bench's generator lane and every device this installation holds there are given back on the wire first, so the hardware is released rather than abandoned to the server's own clean-up. Every module measuring through that bench stops, exactly as it would if the connection had died.
Remove Forgets the selected server - and only one that was added by hand. A server learned from another server's peer table is not removable: the next round would put it straight back, so the button would look broken rather than restrictive. Removing the server you are connected to disconnects it first.
No proxy row here. The desktop dialog has one; this one does not, and nothing is missing. A browser's WebSocket follows the system's own proxy configuration and gives the page no say in it, so there would be nothing for you to set.

Below the buttons sits Add a server by address - a Host: field, a Port: field and an Add button - and a Close button:

RowNotes
Add a server by address (Host:, Port: and the Add button) How every server gets into this list. Port is pre-filled with 8377, the server's default - one port carries everything, so there is only ever one number to type. Add does not simply believe you: it asks the address for its name and identity, and only then remembers the server, under the name it reported. An address that answers but is not a Phonalyser server is refused as such, and so is an empty address or a port outside 1-65535. It then reads that server's peer table and remembers every server it names, so one address usually brings the whole room in at once. A successful add clears the address field, ready for the next one.
What is staged, and what is not. Servers you add or remove are edits - they follow the Preferences dialog behind this window: OK writes them, Cancel drops them. Connecting and disconnecting are actions and take effect at once; a Cancel afterwards does not hand a bench back or take one away. The bench you are connected to is remembered whatever you press, so a Cancel can never lose the only entry for a server you would have to look up again.
An https:// page can only reach a bench on this machine. A bench session is a plain ws:// WebSocket, and a browser refuses to open one from a page it loaded over https:// - that is the mixed-content rule, and it is enforced before anything is dialled, so no timeout tells you what happened. The app says so instead of leaving you to guess. Loopback is exempt, so localhost, 127.0.0.1 and ::1 still connect from an https page; any bench on the LAN does not. The way around it is not a setting - it is to load the app from the bench itself, which serves its own copy over http://; see Phonalyser in a browser ▸ The page a bench serves.

Choosing a bench backend

A connected server's backends join the Backend list beside the local ones, written <server> -> <backend> - e.g. bench-pc -> QA40x. Only backends the bench can really operate are offered: one that is in the server's build but cannot run on that machine is left out rather than shown greyed.

Cards and calibration on the bench

Calibration belongs to the hardware, so with a bench backend selected the Card section works against the bench's store rather than this machine's:

What else crosses the wire

A connection that drops comes back by itself. A session that ended without you asking it to is retried every five seconds until you say otherwise, and the backend you had committed is committed again on the new session - so when you next press Record or Play, it simply works. It deliberately does not restart a measurement: a bench that resumed measuring while nobody was looking would be worse than one that waited. A Disconnect you pressed stays disconnected - that is a decision, not a fault. When a session ends while you are measuring through it, one translated dialog says so and names the reason: the server stopped answering, the connection was closed or failed, the server refused the session, or it sent audio this client cannot read.
If the server loses captured samples before they reach the network, the FFT raises its ⚠ Bench lost audio - averaging suspect banner: the samples on either side of the gap are not adjacent in time, so an average taken across the splice reports a noise floor and a phase that never existed. Reset the statistics to collect a clean one.

Editing the settings as JSON

Both of the documents this dialog writes - the settings and the card profiles - can be opened directly, as text, from the Tools menu, in an editor that knows their shape and explains every key. That has a chapter of its own: Editing the settings as JSON.

Look & Feel

Preferences - Look & Feel tab

SettingNotes
Main tab orientation: Top (a conventional tab strip) or Left (a vertical sidebar of large icon buttons). Applies live - no restart.
Small icons in main tab Shrinks the main-tab icons. Applies live.
Show tips at startup Whether the tip of the day appears when the application opens. Clearing the tip window's own Don't show again box does the same thing from the other side.
UI font / UI font (bold) Fonts for axis labels, readouts and measurement tables (bold for emphasised table text). Applied on OK.

Oscilloscope

Preferences - Oscilloscope tab

SettingNotes
Measurement average, s Window length over which the scope's avg / min / max / σ statistics are accumulated.
Trace line width, pxStroke width of the waveform.
Sample dot diameter, px Size of the per-sample dots drawn when zoomed in.
Histogram bins How many bars the amplitude-histogram panel draws (default 50). Display resolution only: the collection underneath is far finer, so changing this re-draws what has already been collected instead of discarding it.
Display persistence Digital-phosphor afterglow - how long each swept trace lingers before fading, like an analog scope. Choose Off; a preset decay time - 0.5 s, 1 s, 2 s, 5 s, 10 s, 15 s or 20 s (an old trace dims to ~37 % after that time and ~5 % after three times it); (traces never fade, accumulating until the view changes); or Manual (use the field below). Shows trigger jitter and noise as a fading envelope. Persistence is drawn on the GPU and needs WebGL2 with the EXT_color_buffer_float extension. There is no setting for it: where the browser or the graphics driver does not offer that combination the trace simply does not persist - no error, no greyed control, just a scope that redraws each sweep clean.
Manual persistence, s Custom decay time, 0.1-60 s in 0.5-s steps, used when persistence is set to Manual; greyed out otherwise.
Left / Right channel colour Trace colour per channel (defaults cyan #28DCF0 / yellow #F0DC28).

FFT

Preferences - FFT tab

SettingNotes
Trace line width, pxStroke width of the spectrum.
Harmonic dot diameter, px Size of the fundamental / harmonic marker dots.
Multi-tone detect threshold: How far below the strongest peak a second peak may sit and still be tracked as its own tone (vs treated as a harmonic) - the single- vs multi-tone routing in Theory ▸ choosing the phase reference.
Line colourSpectrum trace (default blue #0064C8).
Chart background colourPlot background (default white).
Harmonic dot colour The harmonic / fundamental dots - with a calibration loaded this is the corrected (red) marker. Default red #FF0000.
Before-calibration dot colour The second marker showing where each peak sat before the loaded calibration was de-embedded - see the red / blue calibration dots.
Calibration overlay colour: Colour of the drawn calibration / correction curve.
Filter response line colour The overlaid mains-comb / filter response (default green #009600).

Frequency response

Preferences - Frequency response tab

SettingNotes
Trace line width, px Thickness of the magnitude, phase, RIAA-reference and compare curves.
Maximal analysed frequency, % Nyquist Caps the view's upper bound at this fraction of Nyquist (96-100 %); below 100 % trims the band where the sweep deconvolution rolls off.
Compare smoothing window, 1/N octave Width of the moving average on the compare-mode (measured − reference) curve. N = 0 disables it; smaller N = wider = more smoothing (N ≈ 3 broad, N ≈ 24 light).
Allow notch filter for industrial noise When on, interpolates across each harmonic of the chosen mains frequency to remove hum spikes from the displayed response without re-measuring.
Industrial frequency The base mains frequency (50 / 60 Hz) whose harmonics are notched when the box above is on.
Signal / Phase / Reference (RIAA) / Chart background colour Per-curve and background colours for the frequency-response view.