{"id":20010618,"url":"https://github.com/bradhowes/sf2libau","last_synced_at":"2026-05-15T07:03:42.156Z","repository":{"id":200333995,"uuid":"705288626","full_name":"bradhowes/SF2LibAU","owner":"bradhowes","description":"AUv3 MIDI instrument with sound font (SF2) rendering","archived":false,"fork":false,"pushed_at":"2023-10-22T17:59:53.000Z","size":7632,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-12T14:47:06.794Z","etag":null,"topics":["audio-processing","auv3","coreaudio","ios","macos","sf2","soundfont"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bradhowes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-10-15T15:47:30.000Z","updated_at":"2024-02-14T19:40:29.000Z","dependencies_parsed_at":"2023-12-07T04:41:29.541Z","dependency_job_id":null,"html_url":"https://github.com/bradhowes/SF2LibAU","commit_stats":{"total_commits":18,"total_committers":1,"mean_commits":18.0,"dds":0.0,"last_synced_commit":"e4adebbbf15419083cd553b31e495b31173214f5"},"previous_names":["bradhowes/sf2libau"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradhowes%2FSF2LibAU","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradhowes%2FSF2LibAU/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradhowes%2FSF2LibAU/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradhowes%2FSF2LibAU/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bradhowes","download_url":"https://codeload.github.com/bradhowes/SF2LibAU/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241447523,"owners_count":19964314,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio-processing","auv3","coreaudio","ios","macos","sf2","soundfont"],"created_at":"2024-11-13T07:20:51.244Z","updated_at":"2026-05-15T07:03:42.150Z","avatar_url":"https://github.com/bradhowes.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CI](https://github.com/bradhowes/SF2LibAU/workflows/CI/badge.svg)](https://github.com/bradhowes/SF2LibAU/actions/workflows/CI.yml)\n[![COV](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/bradhowes/6ccc9330abf22f3cc7414190170dca82/raw/SF2LibAU-coverage.json)](https://github.com/bradhowes/SF2LibAU/blob/main/.github/workflows/CI.yml)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbradhowes%2FSF2LibAU%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/bradhowes/SF2LibAU)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbradhowes%2FSF2LibAU%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/bradhowes/SF2LibAU)\n[![License: MIT](https://img.shields.io/badge/License-MIT-A31F34.svg)](https://opensource.org/licenses/MIT)\n\n# SF2LibAU - AUv3 MIDI instrument with sound font (SF2) rendering [BETA]\n\nThe code defines an AUAudioUnit MIDI component in Swift that uses the \n[SF2Lib][sf2lib] engine for rendering audio samples.\n\nCurrently, the AUv3 component:\n\n* responds to MIDI messages and can render audio\n* supports loading of SF2 files via custom sysex MIDI command\n* supports selecting of a preset by bank/program as well as index position\n\nThe unit tests contain examples showing all of the above.\n\nNOTE: requires Swift 5.9 for it's Swift/C++ interoperability facility.\n\n# Parameter Control\n\nAn SF2 file contains a collection of instruments and presets each of which contains a set of values for the SF2 \nsynthesizer parameters (generators) and modulator mappings; those for instruments are absolute values, while the\npreset values are always relative, adding to an instrument's value. Many of these parameters can be modified in\nreal-time by two means:\n\n* AUv3 parameter changes communicated via the [AUParameterTree][tree] instance provided by the AUv3 instrument.\n* MIDI control messages using **NRPN** MIDI control. From the SF2 spec:\n\n\u003e *NRPN* stands for *Non-Registered Parameter Number*. The MIDI specification has defined this series of continuous \n\u003e controllers to permit General MIDI compatible synthesizers to take advantage of their proprietary hardware by using \n\u003e these messages to control the non-General MIDI compatible aspects of their hardware.\n\u003e The [SoundFont 2.01 specification][spec] uses these messages to allow arbitrary real-time control over all \n\u003e SoundFont synthesis parameters.\n\nNote that some MIDI control messages will by default also effect a parameter change. For instance, MIDI pitch bend\nmessages affect the pitch of the note being played, and MIDI controllers 64 (sustain pedal), 66 (soft pedal), and 67\n(sostenuto pedal) affect the envelope behavior of a playing note.\n\n## AUParameterTree\n\nThe [AUParameterTree][tree] for the SF2 engine contains entries for all of the implemented generators. \nSee the [SF spec][spec] for descriptions of these generators and their valid value ranges.\n\nAddress | Name |\n---: | --- |\n0 | startAddrsOffset\n1 | endAddrsOffset\n2 | startloopAddrsOffset\n3 | endloopAddrsOffset\n4 | startAddrsCoarseOffset\n5 | modLfoToPitch\n6 | vibLfoToPitch\n7 | modEnvToPitch\n8 | initialFilterFc\n9 | initialFilterQ\n10 | modLfoToFilterFc\n11 | modEnvToFilterFc\n12 | endAddrsCoarseOffset\n13 | modLfoToVolume\n15 | chorusEffectsSend\n16 | reverbEffectsSend\n17 | pan\n21 | delayModLFO\n22 | freqModLFO\n23 | delayVibLFO\n24 | freqVibLFO\n25 | delayModEnv\n26 | attackModEnv\n27 | holdModEnv\n28 | decayModEnv\n29 | sustainModEnv\n30 | releaseModEnv\n31 | keynumToModEnvHold\n32 | keynumToModEnvDecay\n33 | delayVolEnv\n34 | attackVolEnv\n35 | holdVolEnv\n36 | decayVolEnv\n37 | sustainVolEnv\n38 | releaseVolEnv\n39 | keynumToVolEnvHold\n40 | keynumToVolEnvDecay\n43 | keyRange\n44 | velRange\n45 | startloopAddrsCoarseOffset\n46 | keynum\n47 | velocity\n48 | initialAttenuation\n50 | endloopAddrsCoarseOffset\n51 | coarseTune\n52 | fineTune\n54 | sampleModes\n56 | scaleTuning\n57 | exclusiveClass\n58 | overridingRootKey\n\n\u003e NOTE: any address not listed above will not be found in the AUParameterTree due to gaps in the [SF spec][spec].\n\nAll values for the elements in the AUParameterTree are floating-point values which will be converted into integer values\nthat conform to the spec. For boolean (true/false) settings, values \u003c 0.5 are treated as `false` and values \u003e= 0.5 \n`true`.\n\nThere are additional parameters definitions for MIDI control state:\n\nAddress | Name | Description\n---: | ------------------------- | --------------------------------------------------------------------\n1000 | portamentoModeEnabled     | Portamento mode (aka glide)\n1001 | portamentoRate            | How long it takes to transition for each step\n1002 | oneVoicePerKeyModeEnabled | When enabled, playing same key will cancel previous voice\n1003 | polyphonicModeEnabled     | When enabled, supports playing multiple notes at same time\n1004 | activeVoiceCount          | Reports the number of active voices (read-only)\n1005 | retriggerModeEnabled      | When enabled, playing same voice restarts the envelope of the voice\n\n# Loading SF2 File\n\nThere is a custom SysEx messages that one can use to load an SF2 file and a preset in the file in one shot. To make this\neasy for integration, there is a utility function that will generate the SysEx for a given file path and preset index\nvalue.\n\n```swift\nfunc sendLoadFileUsePreset(path: String, preset: Int) -\u003e Bool\n```\n\nThe function creates the propery SysEx command and then provides it to the sendMIDI utility function that hands it to\n`scheduleMIDIEventBlock` method defined by the audio unit:\n\n```swift\nfunc sendMIDI(bytes: Array\u003cUInt8\u003e, when: AUEventSampleTime = .min, cable: UInt8 = 0) -\u003e Bool {\n  guard let block = scheduleMIDIEventBlock else { return false }\n  block(when, cable, bytes.count, bytes)\n  return true\n}\n```\n\nFor the curious, the actual [format of the SysEx][sysex] is the following:\n\nByte | Field | Description\n---: | -------- | -----------\n0    | 0xF0 | Start of a MIDI 1.0 SysEx message\n1    | 0x7E | Custom SF2Lib command\n2    | 0x00 | Unused subtype (reserved)\n3    | MSB  | the MSB of the preset index to use\n4    | LSB  | the LSB of the preset index to use\n5    | P[0] | the first character of the file path (Base 64 encoded)\nN - 1 | P[N - 1] | the last character of the file path of N encoded characters\nN | 0xF7 | End of a MIDI 1.0 SysEx message\n\nThe file path is Base-64 encoded since MIDI 1.0 data bytes are only 7 bits, even in a SysEx message.\n\nThere is a variant of the above that has no path -- it is used to change to a new preset in the same file:\n\nByte | Field | Description\n---: | -------- | -----------\n0    | 0xF0 | Start of a MIDI 1.0 SysEx message\n1    | 0x7E | Custom SF2Lib command\n2    | 0x00 | Unused subtype (reserved)\n3    | MSB  | the MSB of the preset index to use\n4    | LSB  | the LSB of the preset index to use\n5    | 0xF7 | End of a MIDI 1.0 SysEx message\n\nSince there is no file name, the size of this message is always 6 bytes.\n\n# Selecting Bank/Program\n\nThe above SysEx command is useful when selecting a preset from within the SF2 file, but presets are also \naddressed by a _bank_ and a _program_ value, where a bank contains a collection of programs, and only one bank is\nactive at a time. To change the program in the current bank, there is the MIDI 1.0 programChange command (0xC0) that\ntakes one byte (0-127) that is the value of the program to use in the current bank.\n\nTo switch banks, one can do so by setting two dedicated continuous-controller (CC) values that hold the MSB (0x00) and \nLSB (0x20) of the bank. Both take one byte of value (0-127), so the maximum bank is 128 x 127 + 127 = 16383. To change\na bank and program at the same time, there is the utility function:\n\n```swift\nfunc sendUseBankProgram(bank: UInt16, program: UInt8) -\u003e Bool\n```\n\nThis function actually generates three MIDI commands: 2 to set the dedicated CC values for the bank, and 1 to set the \npreset value.\n\n[sf2lib]: https://github.com/bradhowes/SF2Lib\n[tree]: https://developer.apple.com/documentation/audiotoolbox/auparametertree\n[spec]: https://github.com/bradhowes/SF2Lib/blob/main/SoundFont%20Spec%202.01.pdf\n[sysex]: https://github.com/bradhowes/SF2Lib/blob/4da66ba295a4881a9fb94a7443a12071f70d0172/Sources/SF2Lib/Render/Engine/Engine.mm#L391\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradhowes%2Fsf2libau","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbradhowes%2Fsf2libau","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradhowes%2Fsf2libau/lists"}