FileOutputBlock

Write processed audio to files.

Overview

FileOutputBlock wraps a Writer implementation to save audio from a DSP graph to disk.

Creating a File Output

#![allow(unused)]
fn main() {
use bbx_dsp::{blocks::FileOutputBlock, graph::GraphBuilder};
use bbx_file::writers::wav::WavFileWriter;

let writer = WavFileWriter::<f32>::new("output.wav", 44100.0, 2)?;

let mut builder = GraphBuilder::<f32>::new(44100.0, 512, 2);
let file_out = builder.add(FileOutputBlock::new(Box::new(writer)));
}

Port Layout

PortDirectionDescription
0InputLeft channel
1InputRight channel (if stereo)
NInputChannel N

Usage Examples

Recording Synthesizer Output

#![allow(unused)]
fn main() {
use bbx_dsp::{blocks::{FileOutputBlock, GainBlock, OscillatorBlock}, graph::GraphBuilder, waveform::Waveform};

let writer = WavFileWriter::new("synth_output.wav", 44100.0, 2)?;

let mut builder = GraphBuilder::<f32>::new(44100.0, 512, 2);

let osc = builder.add(OscillatorBlock::new(440.0, Waveform::Sine, None));
let gain = builder.add(GainBlock::new(-6.0, None));
let file_out = builder.add(FileOutputBlock::new(Box::new(writer)));

builder.connect(osc, 0, gain, 0);
builder.connect(gain, 0, file_out, 0);

let mut graph = builder.build();

// Generate 5 seconds of audio
let samples_per_second = 44100;
let total_samples = samples_per_second * 5;
let buffer_size = 512;
let num_buffers = total_samples / buffer_size;

for _ in 0..num_buffers {
    let mut left = vec![0.0f32; buffer_size];
    let mut right = vec![0.0f32; buffer_size];
    let mut outputs: [&mut [f32]; 2] = [&mut left, &mut right];
    graph.process_buffers(&mut outputs);
}

// Important: finalize to close the file
graph.finalize();
}

Stereo Recording

#![allow(unused)]
fn main() {
use bbx_dsp::{blocks::{FileOutputBlock, OscillatorBlock, PannerBlock}, graph::GraphBuilder, waveform::Waveform};

let writer = WavFileWriter::new("stereo.wav", 44100.0, 2)?;

let mut builder = GraphBuilder::<f32>::new(44100.0, 512, 2);

let osc = builder.add(OscillatorBlock::new(440.0, Waveform::Sine, None));
let pan = builder.add(PannerBlock::new(25.0));  // Slightly right
let file_out = builder.add(FileOutputBlock::new(Box::new(writer)));

builder.connect(osc, 0, pan, 0);
builder.connect(pan, 0, file_out, 0);  // Left
builder.connect(pan, 1, file_out, 1);  // Right

let mut graph = builder.build();
// Process...
graph.finalize();
}

Finalization

Always call finalize() after processing:

#![allow(unused)]
fn main() {
graph.finalize();
}

This:

  • Flushes buffered data
  • Updates file headers (WAV size fields)
  • Closes the file handle

Without finalization, the file may be corrupt.

Non-Blocking I/O

FileOutputBlock uses non-blocking I/O:

  • Audio thread fills buffers
  • Background thread writes to disk
  • No blocking during process()
  • Buffers are flushed during finalize()