Skip to content

Audio files

Treat file operations as control/background work. Capability discovery, writer preflight, opening and closing files, probing metadata, decoding, hashing, and cleanup may allocate or perform blocking I/O; none belongs in an audio callback.

  1. Call getAudioFileCapabilities() and require file_io_available before creating media objects. This query reports provider availability and codec policy without probing a file.
  2. Build an exact AudioFileWriterConfig and call preflightAudioFileWrite() before creating a destination. For this documented evidence, use WAV, 48 kHz, mono, and AudioSampleFormat::Int16 (PCM16).
  3. Create a writer with createAudioFileWriter(), check the returned pointer, open the destination, and write interleaved float samples from the control/background thread. Check the returned frame count and close the writer so its header is finalized.
  4. Call probeAudioFile() to inspect the header without decoding. Treat a missing, unreadable, or corrupt path as an error rather than guessing its format.
  5. Create a reader with createAudioFileReader(), check the pointer, open the path, verify format/rate/channel/frame metadata, and read into a caller-owned buffer. A read can return fewer frames at end-of-file; zero means EOF.
  6. Close the reader and remove a temporary destination only after all checks complete. On an error path, close or remove resources from the same control/background ownership domain.

The reader’s open() and close() operations are explicitly non-audio-thread operations. A prepared transport source can then expose immutable or worker-fed PCM to the callback; the callback must not reopen the file or seek it.

The pinned evidence exercises exactly one provider and tuple: WAV PCM16 through the required libsndfile provider. It writes four mono frames, closes the file, probes and reopens it, verifies 48 kHz/mono/four-frame/16-bit metadata, reads four frames, compares the decoded samples, removes the temporary file, and prints the success line. This is a narrow executable proof, not a claim that every supported container, codec, sample format, or provider combination was run.

example.audio-file-smoke · Product 0.9.0 · Unreleased preview
Source revision: 0238eac1721d820d16ba5390e0e4641391be1d59
// SPDX-License-Identifier: MIT
#include <treefall/audio_file_capabilities.h>
#include <treefall/audio_file_reader.h>
#include <treefall/audio_file_writer.h>

#include <array>
#include <cmath>
#include <filesystem>
#include <iostream>
#include <stdexcept>

namespace {
void require(bool condition, const char* message) {
  if (!condition) throw std::runtime_error(message);
}
} // namespace

int main() {
  const auto path = std::filesystem::temp_directory_path() / "treefall-sdk-audio-file-smoke.wav";
  try {
    const auto capabilities = orpheus::getAudioFileCapabilities();
    require(capabilities.file_io_available, "audio file I/O capability is unavailable");

    constexpr orpheus::AudioFileWriterConfig config{
        .format = orpheus::AudioFileFormat::WAV,
        .sample_rate = 48000,
        .num_channels = 1,
        .sample_format = orpheus::AudioSampleFormat::Int16,
    };
    require(orpheus::preflightAudioFileWrite(config) == orpheus::SessionGraphError::OK,
            "WAV PCM16 preflight failed");

    const std::array<float, 4> samples{0.0f, 0.25f, -0.5f, 1.0f};
    auto writer = orpheus::createAudioFileWriter();
    require(writer != nullptr, "audio writer is unavailable");
    require(writer->open(path.string(), config) == orpheus::SessionGraphError::OK,
            "audio writer open failed");
    const auto written = writer->writeSamples(samples.data(), samples.size());
    require(written.isOk() && *written == samples.size(), "audio write frame count mismatch");
    require(writer->close() == orpheus::SessionGraphError::OK, "audio writer close failed");
    require(writer->getFramesWritten() == 4, "audio writer metadata frame count mismatch");

    const auto probe = orpheus::probeAudioFile(path.string());
    require(probe.isOk() && probe.value.format == orpheus::AudioFileFormat::WAV &&
                probe.value.sample_rate == 48000 && probe.value.num_channels == 1 &&
                probe.value.duration_samples == 4 && probe.value.bit_depth == 16,
            "audio probe metadata mismatch");

    auto reader = orpheus::createAudioFileReader();
    require(reader != nullptr, "audio reader is unavailable");
    const auto opened = reader->open(path.string());
    require(opened.isOk() && opened.value.format == orpheus::AudioFileFormat::WAV &&
                opened.value.sample_rate == 48000 && opened.value.num_channels == 1 &&
                opened.value.duration_samples == 4 && opened.value.bit_depth == 16,
            "audio reader metadata mismatch");
    std::array<float, 4> decoded{};
    const auto read = reader->readSamples(decoded.data(), decoded.size());
    require(read.isOk() && *read == 4, "audio read frame count mismatch");
    for (std::size_t index = 0; index < decoded.size(); ++index) {
      require(std::abs(decoded[index] - samples[index]) < 1.0e-4f, "audio sample mismatch");
    }
    reader->close();
    std::filesystem::remove(path);

    std::cout << "Treefall libsndfile WAV smoke: 4 frames\n";
    return 0;
  } catch (const std::exception& error) {
    std::error_code ignored;
    std::filesystem::remove(path, ignored);
    std::cerr << "audio file smoke failed: " << error.what() << '\n';
    return 1;
  }
}
Treefall libsndfile WAV smoke: 4 frames
Source and producer-reported verification
Source-relative path
examples/audio-file-smoke.cpp
SHA-256
d743e5183d94c79363ad4cd191745563310427898941f03103348bb2f73db20b
Producer-reported verification
executed
Environment
os=darwin/arm64; cmake=cmake version 4.1.0; compiler=Apple clang version 21.0.0 (clang-2100.0.123.102)
Command
./examples-build/audio-file-smoke

If capability discovery is false, the provider is absent, or preflight rejects the tuple, stop and report the unavailable or invalid capability. Do not silently switch to a different provider or imply that an unexecuted codec is covered. Writer and reader factories can also return null in a package without file I/O support.

AudioFileWriterConfig accepts WAV, AIFF, and FLAC containers with the documented sample encodings; FLAC rejects Float32. probeAudioFile() is a header-only control operation and reports an empty hash field for successful metadata. The reader’s decoded buffer is interleaved and may be read from a background stream; transport playback should consume a prepared source instead of making file calls from the callback.

For a deterministic installed-package composition that combines reader, transport, and writer, see offline rendering. The pinned API declarations are in the capability, reader, and writer headers. The broader composition is exercised by the pinned offline package source.