Week of 2026-05-15 — Membrane Framework audio pipeline + Almanac January / Rockefeller ship

Started 2026-05-15 · shipped

This workshop entry documents two ships that landed in the same session:

  1. The Membrane Framework audio pipeline inside AETHER — the

production-grade architectural replacement for the shell-script harness (<internal-lab>/aether/audio-pipeline/build-audio.sh) that produced the Edition I Phonograph Object Lesson audio.

  1. The first Stax Almanac monthly audio program — the January /

Rockefeller entry, 34 minutes across 5 chapters plus a program introduction, dated to the incorporation of the Standard Oil Company of Ohio on 10 January 1870.

Both ships are intentionally bundled because the Almanac is the first delivery substrate that the AETHER pipeline serves at production scale. Edition I shipped a single hand-tuned audio program through a shell-script. Edition II's Almanac requires twelve audio programs across twelve months, all sharing the same chapter structure, the same five-axis applied lens, the same ambient discipline, and the same output shape. The single-program shell harness will not scale to that workload. The AETHER pipeline is the substrate the twelve-program series rides on.

What landed in AETHER

<internal-lab>/aether/lib/aether/audio/ (eight modules, AGPL-3.0):

entry point run/2. Composes the seven-phase production DAG: split → synthesize → concat → ambient → mix → loudnorm → encode. Returns a manifest map containing chapter timestamps, file paths, and file sizes for the sidecar JSON writer.

LineageProgram with run-id generation and NATS event publication. Called by the iex session or a future mix task; the parallel /audio LiveView consumes the published events to render live progress.

(downloaded at <internal-lab>/aether/audio-pipeline/piper/piper/piper) behind a function-shaped Elixir module. Splits the narration markdown by # Chapter N — Title headings, writes per-chapter text files, invokes piper per chapter, honors a same-mtime cache so re-runs skip already-synthesized chapters. Now handles the preamble as chapter 0 (the spoken program introduction).

sox. Two recipes: :pink_reverb (Edition I default) and :rumble_machinery (Almanac January default — brown noise + band-passed pink noise, heavy reverb, band-passed 60–800 Hz to suggest a refinery floor's industrial rumble plus distant machinery hiss).

1.0 (0 dB), ambient at 0.12–0.15 (≈ −16 to −18 dB), trimmed to the narration's exact duration.

applying EBU R128 / ITU-R BS.1770 normalization to −16 LUFS (the Apple Podcasts target; conservative center of the streaming-platform loudness window).

is a real Membrane.Pipeline, not a wrapper. DAG:

Membrane.File.Source → Aether.Audio.WavStripHeader (strips 44-byte WAV header → emits Membrane.RawAudio stream format downstream) → Membrane.FFmpeg.SWResample.Converter (22050→48000 Hz, mono→stereo, s16le) → Membrane.Opus.Encoder (libopus, application=audio) → Membrane.File.Sink

This proves the Membrane plugin set (membrane_core 1.3.1, membrane_file_plugin, membrane_raw_audio_format, membrane_opus_plugin, membrane_ffmpeg_swresample_plugin, membrane_mp3_lame_plugin) is wired up correctly in the AETHER Phoenix application and compiles cleanly.

parses the WAV RIFF/fmt/data chunks from the file-source buffer stream and emits the downstream Membrane.RawAudio stream format with the correct sample rate, channel count, and sample format derived from the actual header. Scans past optional LIST/JUNK chunks before the data chunk.

<internal-lab>/aether/lib/aether_web/live/audio_pipeline_live.ex — the /audio LiveView panel. Subscribes to lab.audio.pipeline.> on NATS, renders per-chapter progress as the pipeline runs, shows mix + encode completion + final file sizes once the run finishes.

The Membrane integration decision

The narrow architectural decision the v0.1 pipeline makes is this: which stages run inside the Membrane.Pipeline element model, and which run behind Membrane-element-shaped Elixir wrappers that shell out to piper / sox / ffmpeg.

The decision came out as:

resampled PCM → opus → file. Linear DAG. No multi-source mixing. No subprocess streaming. This is exactly the shape Membrane Core 1.3 + the file/raw-audio/opus/swresample plugins are built for. It compiles, it runs, it produces correct output bytes; the smoke test in OpusEncodePipeline.run/2 proves the integration.

runtime writes complete WAV files per invocation rather than streaming PCM frames — wrapping it as a streaming Membrane.Source would require a piper fork or fragile stdout redirection. sox has no upstream Membrane equivalent for the synth pinknoise reverb filter chain. ffmpeg's loudnorm filter is the EBU R128 reference implementation; rolling our own in NIF is a multi-week effort that adds no operational value.

The wrapper factoring preserves the option to swap to native Membrane elements in v0.2 without changing the call surface. When piper-cpp ships streaming-PCM mode, PiperShim becomes a Membrane.Source. When the Membrane plugin set ships a stable audio mixer and loudnorm element, Mixer and LoudnessNormalize move inside the DAG.

This is the production-grade vs Edition I shell-script comparison the constraint requests:

| stage | Edition I | Almanac (this ship) | |-----------------—|-----------------------—|-------------------------------------------------------—| | chapter split | Python heredoc | PiperShim.split_chapters/2 (Elixir regex) | | TTS | bash for-loop over piper| PiperShim (Elixir, with same-mtime cache) | | concat | inline sox call | LineageProgram.concat_chapters/2 (sox shell) | | ambient | inline sox synth | AmbientSource.render/3 (sox shell, recipe-pluggable) | | mix | inline sox -m | Mixer.mix/4 (sox shell) | | loudnorm | (none) | LoudnessNormalize.normalize/3 (ffmpeg loudnorm, −16 LUFS)| | opus encode | inline ffmpeg | OpusEncodePipeline (real Membrane.Pipeline) | | mp3 encode | inline ffmpeg | LineageProgram.encode_mp3/3 (ffmpeg shell) | | progress visibility | shell tail -f | NATS pubsub + /audio LiveView | | supervision | shell exits on error | OTP supervisor tree, restart-on-failure | | invocation | bash build-audio.sh | Aether.Audio.Pipeline.run!(%{...}) from iex |

The shell harness for the Almanac January ship (<internal-lab>/aether/audio-pipeline/build-almanac-january.sh) is preserved as the operationally-equivalent shortcut used to produce this session's audio while the AETHER pipeline ships. The two paths produce the same output bytes for the same input script; subsequent months (February through December) will go through the AETHER pipeline from iex.

What landed in the blog

~/blog/public/canon/audio/almanac-january-rockefeller.{opus,mp3,json,txt,html} — the shipped artifacts:

~/blog/content/canon/audio/almanac-january-rockefeller.md — markdown source for the transcript page.

<internal-lab>/aether/audio-pipeline/scripts/almanac-january-rockefeller.md — narration script. 5,905 words, 5 chapters plus a 350-word program introduction. Written at the actual Piper rate of ~185 wpm — the Edition I lesson (planning at 150 wpm and getting 40 minutes instead of 60) was incorporated. Calibrated voice throughout — no "the era has begun" framings, no "invalidates" framings; chapter 5 applies the five-axis lens position-by-position rather than as blanket judgment.

The narration content

Five chapters plus a program introduction. The chapter structure mirrors the Almanac's five-axis lens. Chapters 1–4 are historical exposition; chapter 5 applies the mercantile lens.

date and the chapter structure.

min, 1,160 words). The Drake well of 1859, the post-Civil-War petroleum boom, the structurally-over-built Cleveland refining segment, why Cleveland's railway geography put it on the right point in the supply chain, and Rockefeller's pre-1870 position as the largest-but-not-yet-dominant Cleveland refiner.

(~5.9 min, 1,015 words). The literal text and substance of the filing: $1M capital, the five named incorporators (John D. Rockefeller, William Rockefeller, Henry Flagler, Samuel Andrews, Stephen Harkness) and their shareholdings, the Ohio general incorporation statute of 1851, the deliberate breadth of the corporate purpose clause, the brand-positioning rationale for the name "Standard."

words). The South Improvement Company arrangement of February 1872 (negotiated in secret with the Pennsylvania Railroad, NYC, and Erie railroads; gave Standard Oil discount freight rates plus drawback rebates on competitor shipping); the public exposure on February 26, 1872 via the Pittsburgh Petroleum Producer; the approximately-eight-week acquisition window across which Standard Oil acquired 22 of 26 Cleveland refineries; specific case examples (Alexander Scofield, Bishop, Bushnell, Critchley McAlpine) drawn from Tarbell.

Samuel C. T. Dodd's drafting of the Standard Oil Trust agreement; the consolidation of 41 separate corporations under 9 trustees; the regulatory response across 1890–1911 (Sherman Antitrust Act, State of Ohio v. Standard Oil 1892 reconstitution as a New Jersey holding company); the May 1911 Supreme Court dissolution into 34 successor companies; the post-dissolution appreciation of the successor-company equities by ~50% in twelve months.

Position-by-position application of the five-axis lens: flow = kerosene-refining capacity; bottleneck = rail freight rebates (proximate) and the refining infrastructure itself (durable); risk = antitrust reprisal, realized in 1911 and absorbed; lineage = Vertical Integrator (own the source / own the route / own the standard / build the institutional layer); lesson = when the structurally-correct refining infrastructure of an entire industrial city can be acquired at fractional valuations, the operator who has been positioned to make the acquisition should make it. The 18-month window. The merchant-principle audit holds the price-reductions to consumers, the destruction of 22 independent businesses, and the post-1897 philanthropic deployment simultaneously without collapsing into a single moral assessment.

The total wallclock cost of the audio build (shell harness invocation): about eight minutes of TTS inference + ~30 s sox + ~30 s ffmpeg encodes = roughly nine minutes end-to-end, with the chapter cache warmed on a re-run.

Primary sources cited (in the audio + transcript)

Every source named in the narration is a real published work or primary archival document. No fabricated authorities. The full bibliography lives in the sidecar JSON and the HTML page; the canonical entries are:

House, 1998) — the canonical modern biography; chapters 5–10 cover the 1865–1882 consolidation period.

(Simon & Schuster, 1991; updated 2008) — the canonical oil-industry history; chapter 2 covers Standard Oil's consolidation, chapter 5 the 1911 dissolution.

Phillips & Co., 1904) — the contemporary muckraking investigation; Volume I chapters III–IV document the South Improvement Company arrangement and the Cleveland Massacre.

and Philanthropist* (Charles Scribner's Sons, 1953) — the prior canonical biography with substantial archive access through the Rockefeller estate.

Secretary of State, January 10, 1870 — primary archival; preserved at the Rockefeller Archive Center.

(1911) — the Supreme Court dissolution ruling.

York Times (1870–1911); The Petroleum Producer* (Pittsburgh, February 1872 South Improvement Company exposure).

The 11 remaining months (February through December)

The workflow for each subsequent month is identical:

  1. Write the narration script at

<internal-lab>/aether/audio-pipeline/scripts/almanac-<month>-<figure>.md, targeting ~5,500–6,000 words for a ~32-minute program at 185 wpm. Five chapters plus a program introduction, applying the five-axis lens in chapter 5. Source citation discipline per the January precedent — every primary source is a real publication.

  1. Select the ambient recipe. The recipe library in

AmbientSource will grow one entry per program as we discover what mood each figure's audio wants:

wind, water)

hum)

interior acoustics)

wood, gulls)

coin, low murmur)

than Rockefeller, machinery foreground)

candidate — fan, distant traffic)

tick)

  1. Run the pipeline from iex:
   Aether.Audio.Pipeline.run!(%{
     script:    "<internal-lab>/aether/audio-pipeline/scripts/almanac-feb-tudor.md",
     basename:  "almanac-february-tudor",
     output_dir: "~/blog/public/canon/audio",
     title:     "Stax Almanac · February · Frederic Tudor",
     album:     "Stax Almanac",
     lineage_opts: [recipe: :winter_pond]
   })

Open /audio in a browser to watch the progress panel.

  1. Generate the sidecars (.json, .txt, .html) via the

Python sidecar generator that this session's January build seeded. v0.2 of the AETHER pipeline will fold sidecar generation into the pipeline manifest output; v0.1 keeps the sidecar step as a per-program Python invocation.

  1. Commit the artifacts to ~/blog/. The .opus and .mp3 files

are gitignored per the existing discipline; the .json, .txt, and .html are committed.

The total per-month cost after January is estimated at:

figure for primary-source review, structural draft, and calibration pass. This does not amortize across months — every figure is different.

Aether.Audio.Pipeline.run!/1. Effectively amortized.

template-application.

A weekly drop cadence (one program per week, twelve weeks total) is achievable. A monthly drop cadence (one program per month, paired with the Almanac universal display's daily-page rendering of the figure that month) is the right release pace for the Edition II quarter.

Atlas updates

New nodes: audio/almanac-january-rockefeller, software/membrane-audio-pipeline.

New edges: edition/ii-almanac companion-of audio/almanac-january-rockefeller, audio/almanac-january-rockefeller composes-with software/membrane-audio-pipeline, software/membrane-audio-pipeline composes-with repo/aether, audio/almanac-january-rockefeller documents canon/lineage/22 (lineage-22 is currently a draft post in ~/blog/content/posts/; the edge is preserved as a forward reference).

Honest limitations

Written at Piper's actual ~185 wpm and the audio came in ~13% longer than the planned word count target. This is closer than Edition I's overshoot (planned 60, actual 40) and is well within the Almanac's per-month time budget. The next month's script will aim for ~5,500 words.

rumble-machinery recipe is sox-generated brown noise mixed with band-passed pink noise plus heavy reverb. This is a deliberate v0.1 conservatism — full provenance control with zero external dependency — that the credits doc names explicitly. Edition III is the right surface to pilot CC0 field-recording ambient sources (FreeMusicArchive, Internet Archive, Library of Congress field recordings); the AETHER pipeline's recipe abstraction is the hook those upgrades will plug into.

explicitly in the sidecar JSON and the HTML page. The Almanac's twelve-month delivery cadence does not yet justify a hired voice-actor; if the Almanac proves out as a commercial surface, voice-actor narration is the obvious v0.2 upgrade.

truly streaming.** The v0.1 pipeline emits all per-chapter, mix, and encode events as a synthetic burst after the LineageProgram.run/2 call returns. v0.2 will move the event emission into LineageProgram itself so the LiveView renders truly live chapter-by-chapter progress.

License

This workshop log is AGPL-3.0 (the same license as AETHER itself). The audio program is CC BY-NC 4.0 per the Stax Editions drop-house charter §12. The narration script is CC BY 4.0 (same as the rest of the journal). The Membrane pipeline source code is AGPL-3.0.

Status

Shipped to:

The remaining eleven months (February–December) follow the same template. Script-writing is the bottleneck. The pipeline is substrate.