gtreshchev / RuntimeAudioImporter

Runtime Audio Importer plugin for Unreal Engine. Importing audio of various formats at runtime.
MIT License
308 stars 67 forks source link

Crash when converting OGG to WAV #25

Closed fishcatcher closed 2 years ago

fishcatcher commented 2 years ago

Hi, I'm getting a crash when trying to do the following conversion, OGG -> PCM -> WAV. This is the code I'm using:

    //Read buffer
    TArray<uint8> AudioBuffer;
    if (!FFileHelper::LoadFileToArray(AudioBuffer, *InFilePath)) { return false; }

    //Decode OGG to PCM
    const FEncodedAudioStruct EncodedAudioInfo(AudioBuffer.GetData(), AudioBuffer.Num(), EAudioFormat::Auto);
    if (!VorbisTranscoder::Decode(EncodedAudioInfo, OutDecodedPCM)) { return false; }

    //Convert PCM 32bits to PCM 16bits
    TArray<uint8> PCM(DecodedAudioInfo.PCMInfo.PCMData, DecodedAudioInfo.PCMInfo.PCMDataSize);
    TArray<uint8> Converted;
    URuntimeAudioImporterLibrary::TranscodeRAWDataFromBuffer(PCM, ERAWAudioFormat::Float32, Converted, ERAWAudioFormat::Int16);

    //Encode PCM 16bits to WAV
    FDecodedAudioStruct NewDecoded = DecodedAudioInfo;
    NewDecoded.PCMInfo.PCMData = Converted.GetData();
    NewDecoded.PCMInfo.PCMDataSize = Converted.Num();
    FEncodedAudioStruct EncodedWavInfo;
    FWAVEncodingFormat Format{ EWAVEncodingFormat::FORMAT_PCM, 16 };
    if (!WAVTranscoder::Encode(NewDecoded, EncodedWavInfo, Format)) { return false; }

This code works for .wav, .flac, .mp3 but it crashes when I supply a .ogg file. (It crashes trying to WAVTranscoder::Encode()).

This is the stack crash:

OGG_callstack

This is the crash site:

OGG_crashsite

Any idea what could go wrong here?

gtreshchev commented 2 years ago

Thanks for letting me know, I'll take a look at it over the weekend.

fishcatcher commented 2 years ago

awesome, thanks

fishcatcher commented 2 years ago

https://user-images.githubusercontent.com/18588327/176100343-8a38f752-c104-44b6-97ec-d339a4224d1d.mp4

This is the sample file that causes the crash, (I had to add the .mp4 extension because github doesn't like .ogg)

Caffiendish commented 2 years ago

@fishcatcher Can you play the OGG file with Runtime Importer? I'm getting a null Vorbis Decoder, so I'm just getting a nullptr.

~~LogRuntimeAudioImporter: Error: Unable to initialize OGG Vorbis Decoder LogRuntimeAudioImporter: Error: Something went wrong while decoding Vorbis audio data~~

Never mind, I just didn't replace my file input string, and tried to hand it an MP3 🙄

Caffiendish commented 2 years ago

I imported the OGG with the blueprint function: image

Then saved with the export function: image And got a playable wav.

Both of those functions are callable with C++, is there any particular reason you're doing the work manually? Wouldn't it be easier to just utilise these prebuilt functions?

In any case, perhaps comparing your code with the library's code will help?

gtreshchev commented 2 years ago

@Caffiendish Yes, you can look at the code from the plugin to implement this. But I'll look into your code you wrote above at the weekend.

fishcatcher commented 2 years ago

Thanks, I'll test with the blueprint function. The reason I'm doing it manually is because I need to create a USoundWave from runtime, it's doable in UE4 but still finicky in UE5

Caffiendish commented 2 years ago

If you need to create a USoundWave, you can try the awful branch I made here: https://github.com/Caffiendish/RuntimeAudioImporter/tree/MessyFFT I'm trying to build baked FFT data at runtime, to have all the advantages of USoundWave, with runtime imports, basically.

Or use the RuntimeAudioCompressor, which will output a USoundWave. There's a PR open now for fixing parts of that, actually.

fishcatcher commented 2 years ago

Cool, will check it out. Appreciate your works on this btw.

gtreshchev commented 2 years ago

It looks like you are using an older version of the plugin. In the current version of the plugin, the FDecodedAudioStruct and FEncodedAudioStruct structures should only accept memory data from the heap, not from the stack.

Anyway, to convert to WAV with 32-bit float it looks like this:

void ConvertToWavFloat32()
{
    FDecodedAudioStruct DecodedAudioInfo;

    {
        TArray<uint8>* AudioBuffer = new TArray<uint8>();
        {
            if (!FFileHelper::LoadFileToArray(*AudioBuffer, TEXT("C:/Test/Imported.ogg")))
            {
                UE_LOG(LogTemp, Warning, TEXT("Cannot load file to array"))
                delete AudioBuffer;
                return;
            }

            // Removing unused two unitialized bytes
            AudioBuffer->RemoveAt(AudioBuffer->Num() - 2, 2);
        }

        FEncodedAudioStruct EncodedAudioInfo(AudioBuffer->GetData(), AudioBuffer->Num(), EAudioFormat::Auto);

        if (!URuntimeAudioImporterLibrary::DecodeAudioData(EncodedAudioInfo, DecodedAudioInfo))
        {
            UE_LOG(LogTemp, Warning, TEXT("Cannot decode audio data"))
            return;
        }
    }

    FEncodedAudioStruct EncodedAudioInfo;
    {
        EncodedAudioInfo.AudioFormat = EAudioFormat::Wav;
    }

    if (!URuntimeAudioImporterLibrary::EncodeAudioData(DecodedAudioInfo, EncodedAudioInfo, 100))
    {
        UE_LOG(LogTemp, Warning, TEXT("Cannot encode audio data"))
        return;
    }

    TArray<uint8> OutAudioData = TArray<uint8>(EncodedAudioInfo.AudioData.GetView().GetData(), EncodedAudioInfo.AudioData.GetView().Num());

    if (!FFileHelper::SaveArrayToFile(MoveTemp(OutAudioData), TEXT("C:/Test/Exported.wav")))
    {
        UE_LOG(LogTemp, Warning, TEXT("Cannot save array to file"))
    }
}

To convert to WAV 16-bit int, you need to do an additional conversion (from float 32 to int 16 format) and call WAVTranscoder::Encode manually, as you do, but this cannot be called outside the plugin and the current solution does not yet assume this behavior (although you can of course change the source code of the plugin to suit your needs).

gtreshchev commented 2 years ago

As you can see, the plugin is currently not designed for manual format transcoding and is intended to be used mainly for sound wave playback. So this solution may seem confusing and inconvenient.

But in the future I'll make some handy functions for easy format conversions.

fishcatcher commented 1 year ago

Unsure if you've already axed the compression feature, but in 5.0.2, I'm able to create a USoundWave object by using the engine class Audio::FSoundWavePCMWriter. Basically I used the RuntimeAudioImporter to convert all incoming audio files to PCM format then just feed them to that class. It's working so far.

gtreshchev commented 1 year ago

@fishcatcher I'm planning to remove the compression feature entirely, as it's not cross-platform, unstable, and has garbage collection issues.

I haven't tested Audio::FSoundWavePCMWriter, but in any case for compression it won't fix the problems I wrote about above.