Class NativeAudioPointer
A representation of loaded audio memory at the native side.
When you Load(AudioClip) it is copying audio memory to native side. Each memory area of loaded audio is given an ID. This "pointer" is not really a "memory address pointer" like in C++, but just the mentioned ID. Just a simple integer.
Please do not create an instance of this class on your own. You can only get and keep from calling Load(AudioClip)
Inheritance
System.Object
NativeAudioPointer
Namespace: E7.Native
Assembly: E7.NativeAudio.dll
Syntax
public class NativeAudioPointer
Properties
Length
Cached length in seconds of a loaded audio calculated from PCM byte size and specifications.
Declaration
public float Length { get; }
Property Value
Type | Description |
---|---|
System.Single |
Methods
ToString()
Declaration
public override string ToString()
Returns
Type | Description |
---|---|
System.String |
Overrides
System.Object.ToString()
Unload()
Free up loaded audio memory. You cannot call Play(NativeAudioPointer) using this pointer anymore after unloading. It will throw an exception.
THIS METHOD IS UNSAFE ON ANDROID. Read remarks and use with care!
Declaration
public void Unload()
Remarks
[iOS] Unloads OpenAL audio buffer. If some native sources are currently playing audio memory that you just unload, those tracks will be stopped automatically.
[Android] free
the unmanaged audio data array at native side instantly.
This memory freeing could cause segmentation fault (SIGSEGV) if there are audio tracks currently playing the memory. You have to make sure by yourself there is no native source playing this audio before unloading.
On some higher-end phones, it will not crash but instead you will hear loud glitched audio. This is the sound of play head running over freed memory and it interprets those as sound instead of crashing.
Below are details why this method was not made less dangerous.
So the correct approach should be like this : we have to find out who is using the audio. There could be multiple users playing a single audio memory. And then stop them all before freeing memory, ideally.
However my native implementation for the best latency is to never stop any source, because starting one again cause problems on some phones. The callbacks are always running. We can't stop, won't stop.
The next idea is to let the callback know that it is not good to continue, by setting some kind of "unloaded" flag for each audio on unloading. Then we free the memory immediately as before.
However, those callbacks are on a separated thread. It might be in the middle of copying
audio, and already pass the check we want to do to prevent the copy. Communication by flagging is possible
but it may be too late. This is what results in memcpy
crash in your SIGSEGV crash report.
How about unloading NOT unload the audio instantly when you call unload, but allowing all the playing sources that are using that audio to play this audio memory til the end. at the same time prevents any new user. Then when all current tracks finished, unload at that moment. (By keeping a play count of sorts similar to garbage collection, when reduced to zero while unload flag is true, release the resource.)
Unfortunately again, this means we have to add `if` conditional to the callback function. So it could decide should it free memory or not.
This callback function runs every little audio buffer that will be sent out your speaker, so it is a very hot code path. Performance is very important especially considering the point of Native Audio.
For better assembly code, I have optimized very hard to eliminate all if
,
to reduce the need of branch prediction for CPU.
And I couldn't bring myself to add back an if
that the whole point is just to protect
from SIGSEGV potential from unloading, an operation that
is 1% rare when compared to how many times we play the audio, which triggers the callback over and over.
And what's more, you could prevent the crash manually 100% by just stopping the source you know are playing that audio before unloading. Waiting a moment for all audio to finish before unloading is an option too. I choose this manual work over automatic protection for rare case by adding something to a hot code path.
Finally, what I settled with is that unloading could cause SIGSEGV by design, and it is an unsafe method. I won't fix it. Sorry that it doesn't look polished but it's all for better latency, the whole point of Native Audio. I will do whatever it takes to get to the enqueue buffer call faster in that callback method.
What you have to do is just to be careful not to unload while someone is playing that audio by yourself. The code can't help you since the check would be expensive.
One last warning, if you Stop() then immediately Unload() on the next line of code, it is actually not 100% safe.
The thing that keeps pumping audio to the speaker runs on thread, by callbacks that runs on themselves over and over. But stopping is issued on the main thread. It is basically just setting some flags so that the next time that audio thread came, it stops putting out any more audio. However by the nature of thread it will be concurrent with your main thread.
So for example this situation : if the stop runs, the thread had already pass the check for stop and is putting out audio, then you call unload, then you get SIGSEGV because it is putting out freed memory.
So stop and give it a few frames before unloading to be safe.