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.
Control/background-thread sequence
Section titled “Control/background-thread sequence”- Call
getAudioFileCapabilities()and requirefile_io_availablebefore creating media objects. This query reports provider availability and codec policy without probing a file. - Build an exact
AudioFileWriterConfigand callpreflightAudioFileWrite()before creating a destination. For this documented evidence, use WAV, 48 kHz, mono, andAudioSampleFormat::Int16(PCM16). - 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. - Call
probeAudioFile()to inspect the header without decoding. Treat a missing, unreadable, or corrupt path as an error rather than guessing its format. - 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. - 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.
What the evidence proves
Section titled “What the evidence proves”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.
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.
Public API boundaries
Section titled “Public API boundaries”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.