SFBAudioEngine: decoding, playing and converting audio on Apple platforms
A powerhouse of audio functionality for macOS, iOS, and tvOS.
At a glance
- What is it?
- SFBAudioEngine is a Swift and Objective-C library that decodes, plays, encodes and converts audio on macOS, iOS and tvOS, with support for formats Core Audio leaves out such as Ogg Vorbis, Monkey's Audio, WavPack and DSD. It suits an app that needs broad format support, and it is more machinery than a simple AVAudioPlayer needs.
- Who is it for?
- SFBAudioEngine fits an Apple-platform application that has to open, play, convert or write files beyond what Core Audio handles, whether that is Ogg Vorbis, WavPack, Monkey's Audio or DSD, and its SwiftPM distribution makes it a straightforward dependency to add. It is the wrong choice for an app that only ever plays MP3 or M4A, where AVAudioPlayer is enough, and it is the wrong choice for a server-side converter, since it is tied to Apple's audio stack.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 32 days ago.
- What is it written in?
- Mainly Objective-C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The format list is the reason to look at it
SFBAudioEngine is presented as a powerhouse of audio functionality for macOS, iOS and tvOS, and the feature list is broad: audio decoding, playback, encoding, format conversion, and properties information with metadata editing. It is usable from both Swift and Objective-C, which matters for a library that predates the Swift era and is still consumed from Objective-C code bases.
The decisive detail is the format table. On top of everything Core Audio supports, the library adds Ogg Speex, Ogg Vorbis, Monkey's Audio, Musepack, Shorten, True Audio and WavPack, everything libsndfile supports, DSD to PCM conversion for DSD64, and DSD decoding for DSF and DSDIFF with support for DSD over PCM. FLAC, Ogg Opus and MP3 are natively supported by Core Audio, but the project still ships its own encoders and decoders for those three formats rather than deferring.
That last point is a deliberate choice and it cuts both ways. Owning the decoder means consistent behaviour across the formats that Core Audio handles through different code paths, and it means a bug fix applies everywhere. It also means a larger binary and a larger surface to maintain.
The project is MIT licensed, the primary language is Objective-C with Swift in the topics, and the repository ships a Package.swift, a .spi.yml manifest for the Swift Package Index, a .jazzy.yaml for documentation generation, an ACKNOWLEDGMENTS.md and a LICENSES/ directory, which is the pattern for a library that vendors third-party code.
Decoders are split by output, and three of them decorate others
The decoding architecture is built around a protocol hierarchy, and it is the part of the design that explains why the library can do things like DSD over PCM.
Audio decoders read from an SFBInputSource, which may refer to a file, a buffer or a data object. All of them implement the SFBAudioDecoding protocol. Decoders that produce PCM output additionally implement SFBPCMDecoding, and decoders that produce DSD output implement SFBDSDDecoding. The split by output format rather than by container is the organising idea, because it lets a consumer of decoded audio care about the shape of the samples rather than the file they came from.
Three special subclasses decorate an underlying decoder instance rather than decoding anything themselves. SFBAudioRegionDecoder lets you play and loop a region of a PCM decoder, which is what you want for a track preview. SFBDoPDecoder decorates a DSD decoder to provide DSD over PCM, and SFBDSDPCMDecoder decorates a DSD decoder to provide PCM output from it.
The decorator pattern is doing real work here. A DSD file can be delivered to hardware in several shapes depending on what the device accepts, and rather than writing a separate decoder per combination, the library composes them. For an application that needs to preview a DSD album track, the same decode path can feed a conventional PCM output and a DoP output.
The decoder sources live under a Decoders/ directory in the CSFBAudioEngine source tree, with headers under include/SFBAudioEngine/, so the protocol definitions are public and a third party can write a decoder that fits the same scheme.
Playback runs on AVAudioEngine, with the graph left open
The player, SFBAudioPlayer, does not implement its own audio path. It uses an AVAudioEngine processing graph driven by an AVAudioSourceNode, which is the standard way to push samples into an AVAudioEngine in real time.
The design goal stated for the player is complete player functionality with no required configuration, but with the option of customising the underlying processing graph and receiving rich status notifications through a delegate. That combination is the interesting part. A player that hides the graph entirely cannot have an effect or a filter inserted into the output path, and the delegate callbacks are how an application knows that playback finished, stalled or hit an error without polling.
The quick start example shows how little setup is needed:
import SFBAudioEngine
let player = AudioPlayer()
let url = URL(fileURLWithPath: "example.flac")
try? player.play(url)Note the FLAC in that example, which is one of the formats where the library prefers its own decoder. There is a constraint in the documentation worth knowing before you write code around this: only file URLs are supported.
The Objective-C naming convention is visible throughout the public API, with the SFBAudio prefix, while the Swift examples use the shorter names such as AudioPlayer and AudioFile. Both are part of the same library rather than two implementations.
Encoding writes to a target, and the converter is a three-stage pipeline
Encoding mirrors decoding. Audio encoders read input data and convert it to their output format, and they write to an SFBOutputTarget, which may refer to a file, a buffer or a data object. All encoders implement SFBAudioEncoding, and encoders that consume PCM additionally implement SFBPCMEncoding.
Two limits are stated plainly. There are currently no encoders consuming DSD, so DSD is a decode-only format in this library. And encoders do not support arbitrary input formats: the processing format used by an encoder is derived from a desired format combined with the encoder's settings. In other words you cannot hand an encoder arbitrary PCM and expect it to cope; the settings decide what the encoder will accept.
The converter is where the pieces meet. SFBAudioConverter supports the high level conversion operation, and it works in three stages: it reads PCM audio from a decoder in that decoder's processing format, converts it to an intermediate PCM format, and then writes the intermediate PCM to an encoder, which performs the final conversion to the desired format. The decoder's processing format and the intermediate format must both be PCM, but they do not have to share sample rate, bit depth, channel count or channel layout.
That last point is what makes the pipeline practical. The intermediate step exists precisely so that a source in one layout and a target in another can meet without either endpoint having to handle the conversion itself.
Converting a file, or converting into memory
The simplest conversion is one call, with the output format taken from the file extension:
let inputURL = URL(fileURLWithPath: "music.wav")
let outputURL = URL(fileURLWithPath: "music.flac")
try AudioConverter.convert(inputURL, to: outputURL)When you need the result in memory rather than on disk, you build the pieces yourself. The output target is created first, then an encoder is constructed against it and configured with the settings that describe the wanted format:
let output = OutputTarget.makeForData()
let encoder = try AudioEncoder(outputTarget: output, encoderName: .coreAudio)
encoder.settings = [
.coreAudioFileTypeID: kAudioFileM4AType,
.coreAudioFormatID: kAudioFormatMPEG4AAC,
.coreAudioAudioConverterPropertySettings: [kAudioConverterCodecQuality: kAudioConverterQuality_High]
]
try AudioConverter.convert(inputURL, using: encoder)
// Encoder output is in `output.data`The settings dictionary uses Core Audio constants, so a file type and a format ID are specified separately and the codec quality is set through the audio converter property settings. The comment in the example says the encoder output is in output.data, which is the whole point of the data-backed output target.
Reading properties and metadata follows the same shape, with an AudioFile constructed from a URL and its properties and metadata read off it:
if let audioFile = try? AudioFile(readingPropertiesAndMetadataFrom: url) {
let sampleRate = audioFile.properties.sampleRate
let title = audioFile.metadata.title
}Reading metadata without decoding is the cheap path here, and it matters for a media browser that has to populate a list of ten thousand tracks before playing any of them.
Where SFBAudioEngine is the wrong tool
The first limit is that it only runs where Apple's audio stack runs. macOS 11 or later, iOS 15 or later, or tvOS 15 or later are the stated requirements, and the playback path is built on AVAudioEngine. A command line converter for a Linux server, a web backend, or a desktop tool that has to run on an older macOS is out of reach, no matter how good the format coverage is.
The second is weight. If your application only ever plays MP3 or M4A files that the system already understands, AVAudioPlayer handles that in a few lines and adds nothing to your binary. Pulling in a decoder framework, its own FLAC and Opus implementations and a vendored libsndfile for that is a poor exchange. The library earns its size when the format list is the requirement.
The third is the encoding gap. There are no encoders that consume DSD, so a pipeline that has to produce DSD output is not supported, and the encoder input format is not arbitrary, which means the settings have to match what the encoder accepts. For a conversion workflow with a fixed, known set of outputs that is fine. For a general transcoding service with user-selected targets, it is a limitation to design around.
The fourth is the build story for anything outside Xcode. Swift Package Manager is the supported route, and a manual build is described as cloning the repository and running swift build. There is no Linux CI configuration in the tree listing to suggest the package builds anywhere but on Apple platforms. For a library that vendors third-party codecs, that means every consumer inherits the platform assumption and the size of the vendored code.
Editorial conclusion
SFBAudioEngine fits an Apple-platform application that has to open, play, convert or write files beyond what Core Audio handles, whether that is Ogg Vorbis, WavPack, Monkey's Audio or DSD, and its SwiftPM distribution makes it a straightforward dependency to add. It is the wrong choice for an app that only ever plays MP3 or M4A, where AVAudioPlayer is enough, and it is the wrong choice for a server-side converter, since it is tied to Apple's audio stack. Before adopting it, list the formats your users actually open rather than the formats the project supports, check that the encoder you need exists, since there are currently no encoders consuming DSD, and confirm your deployment target meets macOS 11 or iOS 15, with the last push on 2026-09-21 and version 0.14.0 released on 2026-09-14.
Frequently asked questions
What audio formats does SFBAudioEngine support?
Everything Core Audio supports, plus Ogg Speex, Ogg Vorbis, Monkey's Audio, Musepack, Shorten, True Audio, WavPack, all formats supported by libsndfile, and DSD decoding for DSF and DSDIFF with DSD over PCM support, along with DSD to PCM conversion for DSD64.
How do I install SFBAudioEngine?
Add a package dependency to https://github.com/sbooth/SFBAudioEngine in Xcode, or for a manual build clone the repository and run swift build. The library requires macOS 11.0 or later, iOS 15.0 or later, or tvOS 15.0 or later.
How do I convert an audio file to another format?
Call AudioConverter.convert(inputURL, to: outputURL) and the output format is inferred from the file extension. For conversion into memory, create an OutputTarget with makeForData(), configure an AudioEncoder with your settings, and call AudioConverter.convert(inputURL, using: encoder).
Can SFBAudioEngine encode DSD?
No. There are currently no encoders consuming DSD in SFBAudioEngine. Encoders consume PCM, and their processing format is derived from a desired format combined with the encoder's settings rather than accepting arbitrary input.
What licence is SFBAudioEngine released under?
MIT, with the licence text in LICENSE.txt at the root of the repository and a LICENSES/ directory alongside it for the bundled third-party components.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sbooth-sfbaudioengine)Was this useful?