The iOS counterpart of the Android sample: four synchronized tracks through AudioMixer, master
effects (30-band EQ, compressor, dynamic amp), a vocal chain (compressor → delay → reverb), live
peak meters and a tempo-accuracy readout on stop.
The UI is built in code — no storyboard — so the whole demo is MainViewController.cs.
-p:BuildMobile=trueis required, exactly as for the Android sample: without it the engine projects only expose their desktopnet10.0target framework and the app links the wrong assemblies. See the repository README.
# Simulator (Apple Silicon)
dotnet build OwnAudio/Examples/Ownaudio.Example.iOS/Ownaudio.Example.iOS.csproj \
-c Debug -p:BuildMobile=true -p:RuntimeIdentifier=iossimulator-arm64
# Device
dotnet build OwnAudio/Examples/Ownaudio.Example.iOS/Ownaudio.Example.iOS.csproj \
-c Release -p:BuildMobile=true -p:RuntimeIdentifier=ios-arm64A RuntimeIdentifier has to be passed: the static engine library is picked per RID, and there is
no single .a that covers device and simulator at once.
If your Xcode is newer than the one the installed .NET iOS workload pins, the build stops with
E0191. Add -p:ValidateXcodeVersion=false to get past it, or install the matching Xcode.
xcrun simctl boot "iPhone 17 Pro"
xcrun simctl install booted bin/Debug/net10.0-ios/iossimulator-arm64/Ownaudio.Example.iOS.app
xcrun simctl launch --console-pty booted com.ownaudio.iostestiOS will not make a sound until something claims an audio session. The engine deliberately
does not do this for you — the category is an application-level decision. _configureAudioSession
does it here:
AVAudioSession session = AVAudioSession.SharedInstance();
session.SetCategory(AVAudioSessionCategory.Playback);
session.SetPreferredSampleRate(48000, out _);
session.SetPreferredIOBufferDuration(512.0 / 48000.0, out _);
session.SetActive(true, out _);Without it the stream opens and runs, but the audio goes nowhere and the ringer switch mutes the
app. Info.plist also declares the audio background mode so playback survives backgrounding.
Playback is verified on the simulator: four tracks in sync, peaks moving, zero underruns.
Two things had to change in the library before iOS worked at all, both worth knowing about if you touch the interop layer:
- The P/Invoke library name is
__Internalon iOS, notownaudio_ffi. The engine is a static.alinked into the app, and only__Internalmakes the AOT compiler emit direct references to the symbols. Going through aDllImportResolveranddlsymdoes not work — nothing references the Rust symbols statically, so-dead_stripremoves every one of them during the native link. - The audio callbacks are static
[UnmanagedCallersOnly]entry points that recover their instance from the engine'suser_datapointer. A delegate would need a native-to-managed thunk built at run time, and iOS is AOT-only — that fails with "Attempting to JIT compile method".