Sfizz for Unity is a wrapper around sfizz - an open source audio synthesizer capable of loading virutal instruments in sfz files and generating audio with them live in Unity.
This Unity wrapper takes advantage of the exported C APIs in sfizz.h, and hides the details of making function calls to C++. In addition, this package also comes with a simple audio player which interacts with the audio synthesizer. This player takes care of feeding in audio instructions (either generated by the UI or from a MIDI file) to the native library, then converting the returned buffers to a streaming AudioClip
.
As this is a wrapper for sfizz, users need to obtain their own sfizz library (see section below). Note each target platform will require a specific library built on that platform, and once placed in Unity, the library must be configured accordingly (see plugins).
Unity 2018.4.0 or later versions. Works on:
- Windows
- macOS
- Linux
- iOS
The package is available on the openupm registry.
openupm add dev.f1yingbanana.sfizz-unity
You can also install this via Unity Package Manager with the following git url:
https://github.com/f1yingbanana/sfizz-unity.git#upm
An Asset Store package is in the works.
The sfizz library must be built on each of the Unity project's target platform. On Windows, we are looking for the sfizz.dll file. On iOS/macOS, we are looking for sfizz.dylib. On Linux, sfizz.so. Currently their releases on GitHub do not contain library files we need, so we can either build it ourselves (locally or cloning then using GitHub CI), or ask in their Discord channel (they are very nice and helpful 😇).
See this fork for an example of building it on GitHub for Windows (x86 and x64).
Once we obtained the library files, import them into Unity and set them up following this guide. sfizz-unity is a native plugin, by the way.
There are plenty of free samples online, notably the Salamander Grand Piano, a copy of which is included in this package's piano sample scene. For an in-depth discussion of where to place them, see this section below.
The class SfizzPlayer
takes care of a lot of boilerplate code. It keeps an instance of Sfizz
and converts audio output from it on Update
and fills an AudioClip
for the AudioSource
to play. It also takes care of keeping and resizing buffers for data and latency elimination.
To use it, simply add it as a component to a GameObject
in the scene. We now need to create a script to tell it to load an instrument and also what to play.
using F1yingBanana.SfizzUnity;
using UnityEngine;
public class MusicController : MonoBehaviour {
// Don't forget to link the reference to the SfizzPlayer in the scene!
public SfizzPlayer player;
public string sfzPath;
private void Start() {
player.Sfizz.Load(sfzPath);
}
private void Update() {
player.Sfizz.SendNoteOn(/* delay= */ 0, /* noteNumber= */ 60, /* velocity= */ 64);
}
}
Add the MusicController
component to a GameObject
in the scene and link the sfzzPlayer
. For sfzPath
, enter the absolute path to your downloaded sfz file for now (more on this below).
Press play, and you should hear the middle C being triggered repeatedly.
For a more sophisticated setup, import the piano sample scene in the Package Manager and look at the PianoController
.
At a high level, the sfizz engine tracks audio events - when a note has started or stopped. It then processes these events in batch to produce a buffer of audio data, and clears all the events. The cycle then repeats. There are some subtle differences between the C++ implementation and the C# wrapper, however. These are listed below.
sfizz expects a directory system when loading instrument audio from files, but in builds, Unity packs all used assets tightly together into bundles. This means while everything works in the Unity editor, on any standalone player it is no longer possible for sfizz to find the audio samples. Depending on your needs, there are two solutions.
Use the StreamingAssets folder. Put your sfz file in Assets/StreamingAssets/.../instrument.sfz
. In your controller script, pass in a global path.
string path = Path.GetFullPath(Path.Combine(Application.streamingAssetsPath, ".../instrument.sfz"));
sfizzPlayer.Sfizz.Load(path);
Use the persistent data path. At runtime, extract your files to the persistent data path, create your own directories, then pass in the global path.
No. You can try to build the source code for Sfizz with NDK in this guide. StreamingAssets folder also doesn't work for Android, so you will need a system to extract the audio files to persistentDataPath instead.
You likely have an incompatible sfizz library. Currently this wrapper requires a minimum version of Sfizz 1.2, and if your library is a lower version, you might get this exception. See section to obtain the latest version.
This is likely because porting the function to C# is difficult. Almost all functions in the interface present in Sfizz 1.2 are in the wrapper, but if one that should be in the wrapper is missing, file a feature request.