[iOS] Raw PCM data corrupted with distortion/echo on subsequent recording sessions

Hello!

I am writing to report a problem I ran into when implementing voice recording on iOS via a capture DSP. Here are the details of the issue, and how I resolved it (in case someone runs into this problem).

FMOD Version: 2.03.13
Platform: iPhone SE2, iOS 26.5 ((Deployed via Unity IL2CPP)
API Used: FMOD Core API (Bypassing FMOD Studio and Unity RuntimeManager)

Description

When recording on iOS, the first recording session delivers acceptable audio data. However, if the FMOD::System object remains alive between sessions, any subsequent call to System::recordStart results in digital distortion and an additive echo loop that is baked directly into the raw captured PCM bytes. This issue does not occur on Android, high-level code is identical as the same Unity project is built for both iOS and Android.

Context & Implementation Detail

The recording pipeline captures raw microphone data dynamically via an DSP_READ_CALLBACK mapped to a custom DSP on a recording ChannelGroup. The raw PCM float data is pulled directly from this stream.

The corruption and echo are present in the raw bytes immediately upon exiting the DSP callback, before any external encoders or processing layers touch the data.

Only the Core API is used in this pipeline, nothing else. Here is what was added to the UnityAppController.mm’s startUnity method:

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil]; [[AVAudioSession sharedInstance] setActive:YES error:nil];

Removing the DefaultToSpeaker option did not resolve the issue.

Steps to Reproduce

  1. Initialize the FMOD::System Core context on an iOS device.
  2. Call System::recordStart using a designated recording driver.
  3. Read raw PCM floats via the custom DSP callback. Result on Run 1: Audio bytes are clean.
  4. Stop the recording session using System::recordStop.
  5. Start a second recording session using System::recordStart under the same active FMOD::System context.
  6. Read the raw PCM floats again. Result on Run 2+: The raw bytes are immediately corrupted with distortion and a compounding echo loop.

Attempted Fixes That Failed to Resolve the Issue

  1. Using a large, non-looping (loop: false) user sound buffer instead of a looping buffer.
  2. Switching the target recording device index from Driver 0 (Core Audio input) to Driver 1 (Core Audio input (Voice)).
  3. Calling System::mixerSuspend() upon stopping and System::mixerResume() before restarting. This actually caused the distortion to appear on the very first run, signalling that maybe some residual data is not cleaned in a correct way.

Workaround

The only way to get clean raw bytes on subsequent recordings is to completely destroy and rebuild the core system context between recording sessions:

  1. Call System::release() on the active FMOD::System instance.
  2. Instantiate a brand-new system object via FMOD::System_Create().
  3. Re-initialize the Core API context from scratch before calling System::recordStart again.

In order to achieve control over the creation and release of the FMOD::System core in the context of Unity, the RuntimeManager was turned off and instantiation was disabled by editing the source code of the Unity implementation.

Thank you for bringing this to our attention and the detailed post.

Is it possible to check if the issue is present in our record CORE API example:

and let me know if the issue persists? Could I also please grab your Unity version?

Thank you for sharing your workaround.

Hello, Connor!

Thank you for your reply, much appreciated.

I did as you asked and gave it a go with the Core API examples from the website.

I had to change the info.plistand record.ccto make it work on iOS 26.5 and request the correct permissions.

I did get some echo and progressively worse artifacts initially, but it turned out to be due to usingwithOptions:AVAudioSessionCategoryOptionDefaultToSpeakerand the device microphone not filtering out its own output during recording.

Everything worked correctly once I removed it and forwarded sound to the isolated call speaker. No echo, no artifacts, perfectly fine.

Just to be sure, I turned off withOptions:AVAudioSessionCategoryOptionDefaultToSpeakeron the Unity project once again to check if it was the source of the issue. The issue persists on the Unity project, as described in the report, even with the call speaker as output device.

I am using Unity 6000.3.11f1.

I hope this helps!

Regards,

Vasil

Thank you for testing that. Unfortunately, I was not able to reproduce the issue.

In our Unity Integration | Examples Record we playback the sound on a channel rather than reading the PCM data. Could you test if that works without any artifacts? That example uses the Core API so you can still keep some of your current system as well.

Hello, Connor!

Apologies for the delayed reply.

Yes, the example on your website works fine, no issues there.

I did some additional testing and created a barebones example project that uses a capture DSP to replicate the issue. I confirmed the original reported bug, it is still happening, and I managed to reproduce it on Windows in the Unity Editor as well.

On Linux (EndeavourOS with PulseAudio in the Unity Editor) and on Android (11+ with AAudio, on Samsung M51), there are no issues with repeated recording and playback. Works smoothly every time (as reported).

However, repeated recordings on iOS (with CoreAudio on iPhone SE2) and even on Windows 10 (with WASAPI in the Unity Editor) introduce artifacts.

While the iOS problem was expected, the Windows one was not - I tested on two different Windows machines (Dell Latitude 5320 and an Intel NUC 12 Extreme Dragon i9) and repeated recordings did introduce noise and artifacts.

Notably, this happens almost right off the bat on the Dell machine (which is a lot more modest in terms of hardware), and requires 3-4 attempts on the NUC machine until the bug is replicated.

I am guessing some buffer/DSP clean up jobs are dropped on the slower machine, which the faster one picks up somehow. On the same NUC machine, when you run the project under Linux, no such artifacts appear and everything works smoothly.

I can send you the example Unity project I created to replicate the issue. It is just a single scene with a couple of buttons for recording/playback. If you are interested, please let me know what would be the most appropriate way to send you the code.

Regards,

Vasil

Hey Vasil,

A project with a consistent repro would be great! You can upload it to the Uploads tab of your FMOD user profile. Uploads here are only accessible by yourself and FMOD staff.

Hello, Leah!

Thank you for the reply. I just submitted a request to register the project I am working on in order to unlock the option to upload.

I will post a message here, when I have successfully uploaded the project archive.

Have a great day!

Regards,

Vasil

Hello, Leah!

I uploaded an archive of the git repo containing the test project in the Uploadssection of my profile.

The name of the archive is DebugRecordingIssue.zip.

I added a brief README.MD in the archive that explains the structure of the project and some additional information.

Please let me know, if you run into any problems or need more information.

Have a great weekend!

Regards,

Vasil

Sorry for the delayed response.

Thanks for uploading your repro project! I’ve been able to reproduce the issue on both Windows PIE and iOS.

I suspect this is the case, especially since you’re using UniTask. Your FMOD code appears to be sound, and there should be no old samples lingering in the mixer since you’re creating a new recording sound each time, so it’s very likely that either Actor.recordingService or Recording._recordingContainer aren’t being cleared out properly when recording is reinitialized. I’ll continue to investigate and get back to you soon.