Skip to content

Instantly share code, notes, and snippets.

@ashe23
Created May 16, 2018 09:05
Show Gist options
  • Select an option

  • Save ashe23/82c334fabed6ac5e4e0dfea82b986ea0 to your computer and use it in GitHub Desktop.

Select an option

Save ashe23/82c334fabed6ac5e4e0dfea82b986ea0 to your computer and use it in GitHub Desktop.
Audio Decode in UE4
// 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
// 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);
};
@kaizervamph
Copy link

not work on 4.26 :c

@ashe23
Copy link
Author

ashe23 commented Feb 13, 2023

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