Created
May 16, 2018 09:05
-
-
Save ashe23/82c334fabed6ac5e4e0dfea82b986ea0 to your computer and use it in GitHub Desktop.
Audio Decode in UE4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Fill out your copyright notice in the Description page of Project Settings. | |
| #include <AudioActor.h> | |
| #include <Sound/SoundWave.h> | |
| #include <Sound/SoundCue.h> | |
| #include <ConstructorHelpers.h> | |
| #include <WaveFileManager.h> | |
| #include <Runtime/Engine/Classes/Components/AudioComponent.h> | |
| #include <Runtime/Engine/Public/AudioDecompress.h> | |
| #include <Engine.h> | |
| #include <AudioDevice.h> | |
| struct AudioBuffer | |
| { | |
| float StartTime; | |
| float EndTime; | |
| USoundWave *Wave; | |
| }; | |
| // Get wave duration | |
| // if( Endtime - starttime < Wave.Duration ) { | |
| // duration - FullSize | |
| // actualduration - desiredsize | |
| // DesiredSize = FullSize * (EndTime- Starttime) / Wave.Duration; | |
| // } | |
| // else { | |
| // DesiredSize = Wave.PCMDataSize; | |
| // } | |
| // Sets default values | |
| AAudioActor::AAudioActor() | |
| { | |
| // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. | |
| PrimaryActorTick.bCanEverTick = true; | |
| // Initializing AudioComponent | |
| //AudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("AudioComponent")); | |
| //AudioComp1 = CreateDefaultSubobject<UAudioComponent>(TEXT("AudioComp1")); | |
| //AudioComp2 = CreateDefaultSubobject<UAudioComponent>(TEXT("AudioComp2")); | |
| //AudioComp1Wave = CreateDefaultSubobject<USoundWave>(TEXT("AudioComp1Wave")); | |
| //AudioComp2Wave = CreateDefaultSubobject<USoundWave>(TEXT("AudioComp2Wave")); | |
| //Wave = CreateDefaultSubobject<USoundWave>(TEXT("SoundWave")); | |
| //UE_LOG(LogTemp,Warning,TEXT("Size of SoundCue list : %d"), SoundCueList.Num()); | |
| // on audio playback start | |
| // set starttime | |
| // on audio playback end | |
| // set endtime | |
| // calc actual played duration , if its less then sound duration, set Played RawPCMDataSize to ActualDuration * RaWPCMDataSize / Duration | |
| // Fill main buffer with given amount of RawPCMdataSize | |
| // keeping all soundcues links | |
| // assigning for every audio component its callback delegates | |
| // every delegate decompresses soundcue => pcm data buffer | |
| //ConstructorHelpers::FObjectFinder<USoundWave> SoundWave(TEXT("SoundWave'/Game/FirstPerson/Audio/test.test'")); | |
| //ConstructorHelpers::FObjectFinder<USoundCue> FireSoundCue(TEXT("SoundCue'/Game/FirstPerson/Audio/FireCue.FireCue'")); | |
| //ConstructorHelpers::FObjectFinder<USoundCue> TestSoundCue(TEXT("SoundCue'/Game/FirstPerson/Audio/TestCue.TestCue'")); | |
| //if (TestSoundCue.Succeeded()) | |
| //{ | |
| // AudioComp1->SetSound(TestSoundCue.Object); | |
| //} | |
| //if (FireSoundCue.Succeeded()) | |
| //{ | |
| // AudioComp2->SetSound(FireSoundCue.Object); | |
| //} | |
| } | |
| // Called when the game starts or when spawned | |
| void AAudioActor::BeginPlay() | |
| { | |
| Super::BeginPlay(); | |
| init(); | |
| // First we should register delegate | |
| //AudioComp1->OnAudioPlaybackPercent.AddDynamic(this, &AAudioActor::DelegeteCallback1); | |
| //AudioComp2->OnAudioPlaybackPercent.AddDynamic(this, &AAudioActor::DelegeteCallback2); | |
| //AudioComp1->OnAudioFinished.AddDynamic(this, &AAudioActor::DelegateFinished1); | |
| //AudioComp2->OnAudioFinished.AddDynamic(this, &AAudioActor::DelegateFinished2); | |
| //// Starting audio component playing | |
| //AudioComp1->Play(); | |
| //AudioComp2->Play(); | |
| //// initializing timer | |
| //if (GetWorld()) | |
| //{ | |
| // StartTimeSeconds = UGameplayStatics::GetRealTimeSeconds(GetWorld()); | |
| //} | |
| } | |
| void AAudioActor::EndPlay(const EEndPlayReason::Type EndPlayReason) | |
| { | |
| //if (GetWorld()) | |
| //{ | |
| // EndTimeSeconds = UGameplayStatics::GetRealTimeSeconds(GetWorld()); | |
| //} | |
| //UE_LOG(LogTemp, Warning, TEXT("Time last: %f"), EndTimeSeconds - StartTimeSeconds); | |
| //TArray<uint8> Tmp1; | |
| //TArray<uint8> Tmp2; | |
| ////if (AudioComp1Wave->RawPCMData) | |
| ////{ | |
| //// Tmp1.Append(AudioComp1Wave->RawPCMData, AudioComp1Wave->RawPCMDataSize); | |
| //// Tmp2.Append(AudioComp1Wave->RawPCMData, AudioComp1Wave->RawPCMDataSize); | |
| //// //RawPCMBuffer.Append(AudioComp1Wave->RawPCMData, AudioComp1Wave->RawPCMDataSize); | |
| //// UE_LOG(LogTemp, Warning, TEXT("Audio Wave 1 loaded")); | |
| //// UE_LOG(LogTemp, Warning, TEXT("Audio Wave 1 Size: %d"), AudioComp1Wave->RawPCMDataSize); | |
| ////} | |
| //if (AudioComp2Wave->RawPCMData) | |
| //{ | |
| // Tmp1.Append(AudioComp2Wave->RawPCMData, AudioComp2Wave->RawPCMDataSize); | |
| // Tmp1.AddZeroed(1000); | |
| // Tmp1.Append(AudioComp2Wave->RawPCMData, AudioComp2Wave->RawPCMDataSize); | |
| // //RawPCMBuffer.Append(AudioComp2Wave->RawPCMData, AudioComp2Wave->RawPCMDataSize); | |
| // UE_LOG(LogTemp, Warning, TEXT("Audio Wave 2 loaded")); | |
| // UE_LOG(LogTemp, Warning, TEXT("Audio Wave 2 Size: %d"), AudioComp2Wave->RawPCMDataSize); | |
| //} | |
| //// mixing buffers | |
| //int32 range = FMath::Min(Tmp1.Num(), Tmp2.Num()); | |
| // | |
| //for (int i = 0; i < Tmp1.Num(); ++i) | |
| //{ | |
| // //uint8 MixedVal = MixSamples(Tmp1[i], Tmp2[i]); | |
| // //UE_LOG(LogTemp, Warning, TEXT("Val1: %hi , Val2: %hi , => Mixed: %hi"), Tmp1[i], Tmp2[i], MixedVal); | |
| // MainBuffer.Add(Tmp1[i]); | |
| //} | |
| ////int32 range_max = FMath::Max(Tmp1.Num(), Tmp2.Num()); | |
| ////TArray<uint8>& Working = Tmp1.Num() > Tmp2.Num() ? Tmp1 : Tmp2; | |
| ////for (int i = range; i < range_max; ++i) | |
| ////{ | |
| //// MainBuffer.Add(Working[i]); | |
| ////} | |
| //WaveFileManager WFManager; | |
| //uint8* Data = (uint8*)MainBuffer.GetData(); | |
| //TArray<uint8> rr; | |
| //WFManager.Serialize(rr,Data, MainBuffer.Num()); | |
| } | |
| // Called every frame | |
| void AAudioActor::Tick(float DeltaTime) | |
| { | |
| Super::Tick(DeltaTime); | |
| } | |
| void AAudioActor::DelegeteCallback1(const class USoundWave* PlayingSoundWave, const float PlaybackPercent) | |
| { | |
| UE_LOG(LogTemp, Warning, TEXT("Audio Wave callback1")); | |
| if (PlayingSoundWave) | |
| { | |
| AudioComp1Wave = const_cast<USoundWave*>(PlayingSoundWave); | |
| if (PlayingSoundWave->RawPCMData == NULL) | |
| { | |
| FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); | |
| if (AudioDevice) | |
| { | |
| EDecompressionType DecompressionType = AudioComp1Wave->DecompressionType; | |
| AudioComp1Wave->DecompressionType = DTYPE_Native; | |
| FName AudioResourceFormat = AudioDevice->GetRuntimeFormat(AudioComp1Wave); | |
| if ( | |
| AudioComp1Wave->InitAudioResource(AudioResourceFormat) && | |
| (AudioComp1Wave->DecompressionType != DTYPE_RealTime || AudioComp1Wave->CachedRealtimeFirstBuffer == nullptr) | |
| ) | |
| { | |
| FAsyncAudioDecompress TempDecompressor(AudioComp1Wave); | |
| TempDecompressor.StartSynchronousTask(); | |
| } | |
| AudioComp1Wave->DecompressionType = DecompressionType; | |
| } | |
| } | |
| } | |
| } | |
| void AAudioActor::DelegeteCallback2(const USoundWave * PlayingSoundWave, const float PlaybackPercent) | |
| { | |
| UE_LOG(LogTemp, Warning, TEXT("Audio Wave callback2")); | |
| if (PlayingSoundWave) | |
| { | |
| AudioComp2Wave = const_cast<USoundWave*>(PlayingSoundWave); | |
| if (PlayingSoundWave->RawPCMData == NULL) | |
| { | |
| FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); | |
| if (AudioDevice) | |
| { | |
| EDecompressionType DecompressionType = AudioComp2Wave->DecompressionType; | |
| AudioComp2Wave->DecompressionType = DTYPE_Native; | |
| FName AudioResourceFormat = AudioDevice->GetRuntimeFormat(AudioComp1Wave); | |
| if ( | |
| AudioComp2Wave->InitAudioResource(AudioResourceFormat) && | |
| (AudioComp2Wave->DecompressionType != DTYPE_RealTime || AudioComp2Wave->CachedRealtimeFirstBuffer == nullptr) | |
| ) | |
| { | |
| FAsyncAudioDecompress TempDecompressor(AudioComp2Wave); | |
| TempDecompressor.StartSynchronousTask(); | |
| } | |
| AudioComp2Wave->DecompressionType = DecompressionType; | |
| } | |
| } | |
| } | |
| } | |
| void AAudioActor::PlayBackDelegate(const USoundWave * PlayingSoundWave, const float PlayBackPercent) | |
| { | |
| // On every audio playback start fix the global start time of audio | |
| // on finish fix global end time of audio | |
| USoundWave* TmpWave = const_cast<USoundWave *>(PlayingSoundWave); | |
| DecodeSoundWave(TmpWave); | |
| if (TmpWave->RawPCMData) | |
| { | |
| UE_LOG(LogTemp, Warning, TEXT("Wave File already decoded")); | |
| MainBuffer.Append(TmpWave->RawPCMData, TmpWave->RawPCMDataSize); | |
| if (GetWorld()) | |
| { | |
| float CurrentTime = UGameplayStatics::GetRealTimeSeconds(GetWorld()); | |
| } | |
| } | |
| } | |
| void AAudioActor::PlayBackFinishedDelegate() | |
| { | |
| UE_LOG(LogTemp, Warning, TEXT("Sound finished playing")); | |
| } | |
| void AAudioActor::DelegateFinished1() | |
| { | |
| if (AudioComp1Wave->RawPCMData) | |
| { | |
| UE_LOG(LogTemp, Warning, TEXT("1")); | |
| MainBuffer.Append(AudioComp1Wave->RawPCMData, AudioComp1Wave->RawPCMDataSize); | |
| } | |
| } | |
| void AAudioActor::DelegateFinished2() | |
| { | |
| if (AudioComp2Wave->RawPCMData) | |
| { | |
| UE_LOG(LogTemp, Warning, TEXT("2")); | |
| MainBuffer.Append(AudioComp2Wave->RawPCMData, AudioComp2Wave->RawPCMDataSize); | |
| } | |
| } | |
| // Mixing 2 audio buffers | |
| void AAudioActor::MixBuffers(USoundWave* Buf1, int32 Buf1Size, USoundWave* Buf2, int32 Buf2Size) | |
| { | |
| UE_LOG(LogTemp, Warning, TEXT("%d"), Buf1Size); | |
| UE_LOG(LogTemp, Warning, TEXT("%d"), Buf2Size); | |
| } | |
| void AAudioActor::DelegateCallback(const class USoundWave* PlayingSoundWave, const float PlaybackPercent) | |
| { | |
| // if we already have data filled => do nothing | |
| if (RecordedDataSize != 0) | |
| { | |
| return; | |
| } | |
| if (PlayingSoundWave) | |
| { | |
| Wave = const_cast<USoundWave*>(PlayingSoundWave); | |
| if (PlayingSoundWave->RawPCMData == NULL) | |
| { | |
| FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); | |
| if (AudioDevice) | |
| { | |
| EDecompressionType DecompressionType = Wave->DecompressionType; | |
| Wave->DecompressionType = DTYPE_Native; | |
| FName AudioResourceFormat = AudioDevice->GetRuntimeFormat(Wave); | |
| if ( | |
| Wave->InitAudioResource(AudioResourceFormat) && | |
| (Wave->DecompressionType != DTYPE_RealTime || Wave->CachedRealtimeFirstBuffer == nullptr) | |
| ) | |
| { | |
| FAsyncAudioDecompress TempDecompressor(Wave); | |
| TempDecompressor.StartSynchronousTask(); | |
| } | |
| Wave->DecompressionType = DecompressionType; | |
| } | |
| } | |
| } | |
| } | |
| uint8 AAudioActor::MixSamples(uint8 Sample1, uint8 Sample2) | |
| { | |
| //int Remaped1 = remap_range(Sample1, 0, 255, -1, 1); | |
| //int Remaped2 = remap_range(Sample2, 0, 255, -1, 1); | |
| //float mixed = 0; | |
| //if (Remaped1 < 0.5 && Remaped1 < 0.5) | |
| //{ | |
| // mixed = Remaped1 * Remaped2; | |
| // return remap_range(mixed, -1, 1, 0, 255); | |
| //} | |
| //mixed = 2 * (Remaped1 + Remaped2) - 2 * Remaped1 * Remaped2 - 1; | |
| //return remap_range(mixed, -1, 1, 0 , 255); | |
| // Best of all version | |
| return Sample1 + Sample2 - (Sample1 * Sample2) / 65536; | |
| } | |
| // Registers all delegates on every SoundCue in list | |
| void AAudioActor::init() | |
| { | |
| // TODO shitty hack for not include same audio components multiple times | |
| // UE4 calls this method multiple times dunno why | |
| if (AudioComponents.Num() == SoundCueList.Num()) | |
| { | |
| return; | |
| } | |
| for (const auto &Cue : SoundCueList) | |
| { | |
| // Getting SoundCue name and setting them for AudioComponents | |
| FString CueName = Cue->GetName(); | |
| FName AudioCompName(*CueName); | |
| // create AudioComponent | |
| auto NewAudioComponent = NewObject<UAudioComponent>(this, AudioCompName); | |
| // create SoundWave | |
| auto NewSoundWave = NewObject<USoundWave>(this, TEXT("SoundWave")); | |
| if (NewAudioComponent && NewSoundWave) | |
| { | |
| NewAudioComponent->SetSound(Cue); | |
| AudioComponents.Add(NewAudioComponent); | |
| SoundWaves.Add(NewSoundWave); | |
| } | |
| } | |
| UE_LOG(LogTemp, Warning, TEXT("Size of AudioComponent: %d"), AudioComponents.Num()); | |
| UE_LOG(LogTemp, Warning, TEXT("Size of SoundWaves: %d"), SoundWaves.Num()); | |
| // for every audio component we should register delegate | |
| for (const auto &Comp : AudioComponents) | |
| { | |
| Comp->OnAudioPlaybackPercent.AddDynamic(this, &AAudioActor::PlayBackDelegate); | |
| Comp->OnAudioFinished.AddDynamic(this, &AAudioActor::PlayBackFinishedDelegate); | |
| Comp->Play(); | |
| } | |
| } | |
| float AAudioActor::remap_range(int val, int in_min, int in_max, int out_min, int out_max) | |
| { | |
| return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; | |
| } | |
| // Handles SoundWave decompression | |
| void AAudioActor::DecodeSoundWave(USoundWave * PlayingSoundWave) | |
| { | |
| if (PlayingSoundWave->RawPCMData == NULL) | |
| { | |
| UE_LOG(LogTemp, Warning, TEXT("Sound Wave: %s decoded successfully."), *PlayingSoundWave->GetName()); | |
| FAudioDevice* AudioDevice = GEngine->GetMainAudioDevice(); | |
| if (AudioDevice) | |
| { | |
| EDecompressionType DecompressionType = PlayingSoundWave->DecompressionType; | |
| PlayingSoundWave->DecompressionType = DTYPE_Native; | |
| FName AudioResourceFormat = AudioDevice->GetRuntimeFormat(PlayingSoundWave); | |
| if ( | |
| PlayingSoundWave->InitAudioResource(AudioResourceFormat) && | |
| (PlayingSoundWave->DecompressionType != DTYPE_RealTime || PlayingSoundWave->CachedRealtimeFirstBuffer == nullptr) | |
| ) | |
| { | |
| FAsyncAudioDecompress TempDecompressor(PlayingSoundWave); | |
| TempDecompressor.StartSynchronousTask(); | |
| } | |
| PlayingSoundWave->DecompressionType = DecompressionType; | |
| } | |
| } | |
| } | |
| // keep all level sounds in array | |
| // decode all sounds | |
| // on any sound start keep start time and end time | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Fill out your copyright notice in the Description page of Project Settings. | |
| #pragma once | |
| #include <CoreMinimal.h> | |
| #include <GameFramework/Actor.h> | |
| #include "AudioActor.generated.h" | |
| UCLASS() | |
| class VIRTUAL_FOOTBALL_API AAudioActor : public AActor | |
| { | |
| GENERATED_BODY() | |
| public: | |
| // Sets default values for this actor's properties | |
| AAudioActor(); | |
| UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AudioRecord") | |
| TArray <class USoundCue*> SoundCueList; | |
| UPROPERTY(EditAnywhere) | |
| UAudioComponent* AudioComponent; | |
| UPROPERTY(EditAnywhere) | |
| UAudioComponent* AudioComp1; | |
| USoundWave *AudioComp1Wave; | |
| USoundWave *AudioComp2Wave; | |
| UPROPERTY(EditAnywhere) | |
| UAudioComponent* AudioComp2; | |
| USoundWave *Wave; // this | |
| TArray<uint8> RawPCMBuffer; | |
| // START REFACTORED | |
| TArray<UAudioComponent *> AudioComponents; | |
| TArray<USoundWave *> SoundWaves; | |
| // END REFACTORED | |
| // Hold resulting audio buffer data | |
| TArray<uint8> MainBuffer; | |
| uint8* RecordedData; | |
| int32 RecordedDataSize; | |
| int16* RawPCMBufferData; | |
| protected: | |
| // Called when the game starts or when spawned | |
| virtual void BeginPlay() override; | |
| virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; | |
| public: | |
| // Called every frame | |
| virtual void Tick(float DeltaTime) override; | |
| UFUNCTION() | |
| void DelegateCallback(const class USoundWave* PlayingSoundWave, const float PlaybackPercent); | |
| UFUNCTION() | |
| void DelegeteCallback1(const class USoundWave* PlayingSoundWave, const float PlaybackPercent); | |
| UFUNCTION() | |
| void DelegeteCallback2(const class USoundWave* PlayingSoundWave, const float PlaybackPercent); | |
| UFUNCTION() | |
| void PlayBackDelegate(const class USoundWave* PlayingSoundWave, const float PlayBackPercent); | |
| UFUNCTION() | |
| void PlayBackFinishedDelegate(); | |
| UFUNCTION() | |
| void DelegateFinished1(); | |
| UFUNCTION() | |
| void DelegateFinished2(); | |
| void MixBuffers(USoundWave* Buf1, int32 Buf1Size, USoundWave* Buf2, int32 Buf2Size); | |
| uint8 MixSamples(uint8 Sample1, uint8 Sample2); | |
| UFUNCTION(BlueprintCallable, Category = "AudioRecord") | |
| void init(); | |
| private: | |
| float remap_range(int val, int in_min, int in_max, int out_min, int out_max); | |
| float StartTimeSeconds; | |
| float EndTimeSeconds; | |
| void DecodeSoundWave(class USoundWave* PlayingSoundWave); | |
| }; |
Author
Well this code written 5 years ago, I don`t even remember what engine version it for, must be 4.9-4.11 :D
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
not work on 4.26 :c