naudio / NAudio

Audio and MIDI library for .NET
MIT License
5.59k stars 1.1k forks source link

Getting "InvalidHandle calling acmStreamUnprepareHeader" under Unity IL2CPP build #648

Open xucian opened 4 years ago

xucian commented 4 years ago

Hey, Thanks for the library. I'm using the latest version in Unity3D under Mono and it works perfectly to stream MP3s from disk, both in the editor and in the Windows build.

However, after I build with AOT (Unity's IL2CPP implementation) and run the build, I'm getting an exception. I'm aware that you may not be familiar with Unity or their IL2CPP build system, but maybe the stacktrace would give you a clue about the cause:

MmException: InvalidHandle calling acmStreamUnprepareHeader at NAudio.Wave.Compression.AcmStreamHeader.Unprepare () [0x00000] in <00000000000000000000000000000000>:0 at NAudio.Wave.Compression.AcmStreamHeader.Convert (System.Int32 bytesToConvert, System.Int32& sourceBytesConverted) [0x00000] in <00000000000000000000000000000000>:0 at NAudio.Wave.Compression.AcmStream.Convert (System.Int32 bytesToConvert, System.Int32& sourceBytesConverted) [0x00000] in <00000000000000000000000000000000>:0 at NAudio.Wave.AcmMp3FrameDecompressor.DecompressFrame (NAudio.Wave.Mp3Frame frame, System.Byte[] dest, System.Int32 destOffset) [0x00000] in <00000000000000000000000000000000>:0 at NAudio.Wave.Mp3FileReader.Read (System.Byte[] sampleBuffer, System.Int32 offset, System.Int32 numBytes) [0x00000] in <00000000000000000000000000000000>:0 at NAudio.Wave.SampleProviders.Pcm16BitToSampleProvider.Read (System.Single[] buffer, System.Int32 offset, System.Int32 count) [0x00000] in <00000000000000000000000000000000>:0 at NAudio.Wave.SampleProviders.MeteringSampleProvider.Read (System.Single[] buffer, System.Int32 offset, System.Int32 count) [0x00000] in <00000000000000000000000000000000>:0 at NAudio.Wave.SampleProviders.VolumeSampleProvider.Read (System.Single[] buffer, System.Int32 offset, System.Int32 sampleCount) [0x00000] in <00000000000000000000000000000000>:0 at NAudio.Wave.SampleProviders.SampleChannel.Read (System.Single[] buffer, System.Int32 offset, System.Int32 sampleCount) [0x00000] in <00000000000000000000000000000000>:0 at NAudio.Wave.AudioFileReader.Read (System.Single[] buffer, System.Int32 offset, System.Int32 count) [0x00000] in <00000000000000000000000000000000>:0 at Com.TheFallenGames.DBD.ThirdPartyMyScripts.NAudio.NAudioPlayer.Stream_OnAudioRead (System.Single[] data) [0x00000] in <00000000000000000000000000000000>:0 at System.Xml.CachingEventHandler.Invoke (System.Xml.XsdCachingReader cachingReader) [0x00000] in <00000000000000000000000000000000>:0 at UnityEngine.AudioClip.InvokePCMReaderCallback_Internal (System.Single[] data) [0x00000] in <00000000000000000000000000000000>:0 at UnityEngine.AudioClip.Create (System.String name, System.Int32 lengthSamples, System.Int32 channels, System.Int32 frequency, System.Boolean stream, UnityEngine.AudioClip+PCMReaderCallback pcmreadercallback, UnityEngine.AudioClip+PCMSetPositionCallback pcmsetpositioncallback) [0x00000] in <00000000000000000000000000000000>:0 at Com.TheFallenGames.DBD.ThirdPartyMyScripts.NAudio.NAudioPlayer.CreateStreamed (System.String uri) [0x00000] in <00000000000000000000000000000000>:0 at Com.TheFallenGames.DBD.ThirdPartyMyScripts.NAudio.NAudioPlayer.CreateAudioClip () [0x00000] in <00000000000000000000000000000000>:0

Com.TheFallenGames.DBD.ThirdPartyMyScripts.NAudio.NAudioPlayer is just a helper class I created to access NAudio.

Unity's IL2CPP does code stripping which removes seemingly unused code, so that may be the cause. Users can force some assemblies to never be stripped by providing a link.xml file, and I've done that and included NAudio in it for this purpose - without luck. Maybe there are some assemblies/types from the System namespace that are stripped but needed by NAudio. Just a thought.

Hope this is enough info to find the cause. I see NAudio as the 1st choice when it comes to loading MP3 under Unity, so I'm sure there are others that'll benefit from solving this, if there's any solution.

Here's the code:

AudioClip CreateStreamed(string uri)
{
    _StreamedAudioFileReader = new AudioFileReader(uri);
    var waveFormat = _StreamedAudioFileReader.WaveFormat;
    int channels = waveFormat.Channels;
    int samplesPerSec = waveFormat.SampleRate;
    _DurationMS = _StreamedAudioFileReader.TotalTime.TotalMilliseconds;
    double durationS = _DurationMS / 1000d;
    _TotalSamples = (int)(samplesPerSec * durationS + .5d);
    AudioClip audioClip = AudioClip.Create(Path.GetFileName(uri), _TotalSamples, channels, samplesPerSec, true, Stream_OnAudioRead, Stream_OnAudioSetPosition);

    return audioClip;
}

void Stream_OnAudioRead(float[] data)
{
    _StreamedAudioFileReader.Read(data, 0, data.Length);
}

void Stream_OnAudioSetPosition(int newPosition)
{
    double pos01 = newPosition / (double)_TotalSamples;
    if (pos01 > 1d)
        pos01 = 1d;
    _StreamedAudioFileReader.CurrentTime = TimeSpan.FromMilliseconds(pos01 * _DurationMS);
}
orosbogdan commented 2 months ago

Native AoT would be great for iOS as it doesn't support JIT.