<!-- canonical: https://docs.jmrp.io/phonometry/io/ -->
Source: https://docs.jmrp.io/phonometry/io/

# Files

Every other area of this library starts from an array that is already in
memory; this one is about the files the array comes from and returns to. Its
premise is that an audio file made by a measurement chain is a *measurement
record*: the samples are only meaningful next to the sample rate, the
calibration that maps digital full scale to pascals, and the provenance
that says where, when and through what the recording was made. The module
`phonometry.io` reads and writes with exactly that in mind: it never
resamples, never mixes channels down, never normalizes, and never touches a
level, because each of those is a documented default somewhere in the
ecosystem and each one quietly destroys the quantity a measurement exists
to preserve.

The practical shape follows from an equipment survey rather than from
format completism. Sound level meters and field recorders emit linear WAV:
24-bit PCM, `WAVE_FORMAT_EXTENSIBLE` for multichannel, BWF `bext` metadata,
RF64 once an overnight recording passes 4 GiB. So the **base install**
(NumPy and SciPy alone) reads all of it, and the compressed formats live
behind the optional `[audio]` extra (python-soundfile, whose wheel bundles
libsndfile under the LGPL-2.1). Lossy sources are read but flagged, loudly:
a level computed from an ADPCM or MP3 approximation of the waveform is not
defensible, and the warning plus the `lossy` stamp keep that fact attached
to the data.

What comes back from a file is a `Signal` (`io.Signal`, or
`phonometry.Signal`; the module it is defined in is private): the samples with their rate,
calibration, channel labels and `bext` provenance in one immutable object
that behaves as the bare array everywhere (`np.asarray` hands any existing
function the samples), draws itself with `.plot()`, and walks straight into
the level functions (`leq`, `laeq`, `ln_levels`, `sel`, `lc_peak`,
`sound_exposure`, `lex_8h`) without
repeating the rate or the calibration by hand. In the other direction,
`write()` produces WAV/BWF (24-bit packed in-house) and FLAC, refuses to
clip silently, extends the `CodingHistory` rather than replacing it, can
measure the five EBU R 128 loudness values of `bext` version 2 with the
library's own BS.1770 implementation, and puts the calibration in a small
versioned JSON sidecar beside the audio: the one number no audio container
has a field for, made to travel.

The other files the library reads and writes are catalogues of materials.
The library publishes the tables of its books and standards and none of a
manufacturer's, so a data sheet, a declaration of performance or a test
report of your own goes in a file of yours, a JSON document or a spreadsheet
saved as CSV with a JSON header beside it, with the provenance of the
document: its kind, its version, the day you consulted it, the laboratory
and the report. `read_catalogue` reads that file into rows of the same
classes every published catalogue hands out, so a porous core from your data
sheet goes into `medium()` as one from a book does, and a figure the sheet
prints in kPa·s/m² or in centimetres is converted on its digits and recorded
as the sheet printed it. The problems in a file are reported together, each
at its place in the file, and nothing the file names is ever imported.
`write_catalogue` writes a catalogue back, and writes a published table in
the same form, as a template to start from. `catalogue_schema` writes the
JSON Schema of such a file, so an editor completes one as you type it and
marks what the reader would refuse for its form; the site publishes the
schema of every row class the library publishes.

## Pages in this section

- [Reading and writing measurement audio](https://docs.jmrp.io/phonometry/audio-files/): the
  whole workflow on one runnable page: reading a meter's WAV into a
  calibrated `Signal`, deriving the calibration from the calibrator take,
  the lossy warning and the listening-copy story behind it, streaming an
  overnight recording through stateful filters, writing BWF with its
  provenance, the sidecar round trip, and conversion that keeps the
  measurement a measurement.
- [Your own catalogues](https://docs.jmrp.io/phonometry/material-catalogues/): a data
  sheet, a declaration of performance, a test report or a measurement of
  yours as a catalogue file: a published table as the template, the
  document and its provenance, a spreadsheet saved as CSV, the refusals
  that point at a cell, the
  rows in the models and in every lookup by name, which row class
  holds which quantity, and the JSON Schema an editor completes a file
  with. What each number on a data sheet is, before you
  type it, is in
  [Reading a material datasheet](https://docs.jmrp.io/phonometry/materials/reading-a-datasheet/),
  among the materials guides.

## What this section does not cover

**No playback, no editing, no effects.** This is a file layer for
measurements, not an audio workstation: nothing here plays a sound, trims a
region by ear, or applies gain. There is deliberately no lossy *writing*
either: no measurement ends in an MP3, and a library whose job is
preserving levels should not make one easy to produce.

**AAC/M4A stays external.** Dictaphone and phone recordings in MP4
containers are not decoded; the honest route is a one-line
`ffmpeg -i note.m4a note.wav` and then `read()`, with the same lossy caveat
either way. Likewise the DAQ formats that are not audio at all (TDMS
(LabVIEW) and UFF (modal analysis)) have their own Python readers, `npTDMS`
and `pyuff`, and pretending they are audio files would serve neither.

**Ambisonic files are tolerated, not interpreted.** A B-format `.amb` file
or an ambisonic GUID reads fine and its channels come back labelled, but no
FuMa-to-AmbiX conversion matrix is applied: channel 0 of a FuMa file
carries W at −3 dB, and turning that into a pressure without saying so
would be a silent level change. This is exactly the class of default this
module exists to refuse.

**No manufacturer's data ships with the library.** A data sheet is revised
under the same title, its terms rarely allow copying it, and the value a
project needs is the one on the sheet it was specified against, so your
catalogues stay in your files and in the objects `read_catalogue` hands
back: the library keeps no copy and opens no link a file names. For the same
reason a table of a manufacturer pasted into an issue or a pull request is
never merged.

## Before and after these pages

Before: [Calibration and dBFS](https://docs.jmrp.io/phonometry/signals/metrology/calibration/)
explains where the calibration factor a `Signal` carries actually comes
from, and [Getting started](https://docs.jmrp.io/phonometry/start/getting-started/) runs a first
file through the chain. After: everything else. The point of the file layer
is that [levels](https://docs.jmrp.io/phonometry/signals/levels/levels/),
[octave filtering](https://docs.jmrp.io/phonometry/signals/filters/filter-banks/),
[block processing](https://docs.jmrp.io/phonometry/signals/filters/block-processing/) and the
rest of the library receive a calibrated signal and never learn it came
from a file.

If you arrived here from a search and want the shape of the whole library,
[What do you need to measure?](https://docs.jmrp.io/phonometry/start/tasks/) indexes it by the
job and [All guides](https://docs.jmrp.io/phonometry/) lists every page with a line
on each.
