SIZE_T USoundCue::GetResourceSize(EResourceSizeMode::Type Mode) { if( Mode == EResourceSizeMode::Exclusive ) { return( 0 ); } else { // Sum up the size of referenced waves int32 ResourceSize = 0; TArray<USoundNodeWavePlayer*> WavePlayers; RecursiveFindNode<USoundNodeWavePlayer>( FirstNode, WavePlayers ); for( int32 WaveIndex = 0; WaveIndex < WavePlayers.Num(); ++WaveIndex ) { USoundWave* SoundWave = WavePlayers[WaveIndex]->GetSoundWave(); if (SoundWave) { ResourceSize += SoundWave->GetResourceSize(Mode); } } return( ResourceSize ); } }
EReimportResult::Type UMP3SoundFactory::Reimport(UObject* Obj) { if (Obj == nullptr || Cast<USoundWave>(Obj) == nullptr) { return EReimportResult::Failed; } USoundWave* SoundWave = Cast<USoundWave>(Obj); const FString Filename = SoundWave->AssetImportData->GetFirstFilename(); const FString FileExtension = FPaths::GetExtension(Filename); if (FileExtension.Compare("MP3", ESearchCase::IgnoreCase) != 0) return EReimportResult::Failed; UE_LOG(MP3ImporterLog, Log, TEXT("Reimport triggered for %s"), *Filename); if (UFactory::StaticImportObject(SoundWave->GetClass(), SoundWave->GetOuter(), *SoundWave->GetName(), RF_Public | RF_Standalone, *Filename, nullptr, this)) { UE_LOG(MP3ImporterLog, Log, TEXT("Reimport successfull")); SoundWave->AssetImportData->Update(Filename); SoundWave->MarkPackageDirty(); return EReimportResult::Succeeded; } else { UE_LOG(MP3ImporterLog, Warning, TEXT("Reimport failed!")); return EReimportResult::Failed; } }
void USoundWave::GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const { Super::GetAssetRegistryTags(OutTags); // GetCompressedDataSize could technically modify this->CompressedFormatData therefore it is not const, however this information // is very useful in the asset registry so we will allow GetCompressedDataSize to be modified if the formats do not exist USoundWave* MutableThis = const_cast<USoundWave*>(this); const FString OggSize = FString::Printf(TEXT("%.2f"), MutableThis->GetCompressedDataSize("OGG") / 1024.0f ); OutTags.Add( FAssetRegistryTag("OggSize", OggSize, UObject::FAssetRegistryTag::TT_Numerical) ); }
/** * This will return the name of the SoundClass of the Sound that this buffer(SoundWave) belongs to. * NOTE: This will find the first cue in the ObjectIterator list. So if we are using SoundWaves in multiple * places we will pick up the first one only. **/ FName FSoundBuffer::GetSoundClassName() { // need to look in all cues for (TObjectIterator<USoundBase> It; It; ++It) { USoundCue* Cue = Cast<USoundCue>(*It); if (Cue) { // get all the waves this cue uses TArray<USoundNodeWavePlayer*> WavePlayers; Cue->RecursiveFindNode<USoundNodeWavePlayer>(Cue->FirstNode, WavePlayers); // look through them to see if this cue uses a wave this buffer is bound to, via ResourceID for (int32 WaveIndex = 0; WaveIndex < WavePlayers.Num(); ++WaveIndex) { USoundWave* WaveNode = WavePlayers[WaveIndex]->GetSoundWave(); if (WaveNode != NULL) { if (WaveNode->ResourceID == ResourceID) { if (Cue->GetSoundClass()) { return Cue->GetSoundClass()->GetFName(); } else { return NAME_None; } } } } } else { USoundWave* Wave = Cast<USoundWave>(*It); if (Wave && Wave->ResourceID == ResourceID) { if (Wave->GetSoundClass()) { return Wave->GetSoundClass()->GetFName(); } else { return NAME_None; } } } } return NAME_None; }
void USoundNodeWaveParam::ParseNodes( FAudioDevice* AudioDevice, const UPTRINT NodeWaveInstanceHash, FActiveSound& ActiveSound, const FSoundParseParameters& ParseParams, TArray<FWaveInstance*>& WaveInstances ) { USoundWave* NewWave = NULL; ActiveSound.GetWaveParameter( WaveParameterName, NewWave ); if( NewWave != NULL ) { NewWave->Parse( AudioDevice, GetNodeWaveInstanceHash(NodeWaveInstanceHash, (UPTRINT)NewWave, 0), ActiveSound, ParseParams, WaveInstances ); } else { // use the default node linked to us, if any Super::ParseNodes( AudioDevice, NodeWaveInstanceHash, ActiveSound, ParseParams, WaveInstances ); } }
void USoundWave::GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const { Super::GetAssetRegistryTags(OutTags); #if WITH_EDITORONLY_DATA if (AssetImportData) { OutTags.Add( FAssetRegistryTag(SourceFileTagName(), AssetImportData->GetSourceData().ToJson(), FAssetRegistryTag::TT_Hidden) ); } #endif // GetCompressedDataSize could technically modify this->CompressedFormatData therefore it is not const, however this information // is very useful in the asset registry so we will allow GetCompressedDataSize to be modified if the formats do not exist USoundWave* MutableThis = const_cast<USoundWave*>(this); const FString OggSize = FString::Printf(TEXT("%.2f"), MutableThis->GetCompressedDataSize("OGG") / 1024.0f ); OutTags.Add( FAssetRegistryTag("OggSize", OggSize, UObject::FAssetRegistryTag::TT_Numerical) ); }
int32 USoundCue::GetResourceSizeForFormat(FName Format) { TArray<USoundNodeWavePlayer*> WavePlayers; RecursiveFindNode<USoundNodeWavePlayer>(FirstNode, WavePlayers); int32 ResourceSize = 0; for (int32 WaveIndex = 0; WaveIndex < WavePlayers.Num(); ++WaveIndex) { USoundWave* SoundWave = WavePlayers[WaveIndex]->GetSoundWave(); if (SoundWave) { ResourceSize += SoundWave->GetResourceSizeForFormat(Format); } } return ResourceSize; }
bool FMixerSource::ReadMorePCMRTData(const int32 BufferIndex, EBufferReadMode BufferReadMode, bool* OutLooped) { USoundWave* WaveData = WaveInstance->WaveData; if (WaveData && WaveData->bProcedural) { AUDIO_MIXER_CHECK_AUDIO_PLAT_THREAD(MixerDevice); const int32 MaxSamples = (MONO_PCM_BUFFER_SIZE * Buffer->NumChannels) / sizeof(int16); if (BufferReadMode == EBufferReadMode::Synchronous || WaveData->bCanProcessAsync == false) { const int32 BytesWritten = WaveData->GeneratePCMData(SourceVoiceBuffers[BufferIndex]->AudioData.GetData(), MaxSamples); SourceVoiceBuffers[BufferIndex]->AudioBytes = BytesWritten; } else { check(!AsyncRealtimeAudioTask); AsyncRealtimeAudioTask = new FAsyncRealtimeAudioTask(WaveData, SourceVoiceBuffers[BufferIndex]->AudioData.GetData(), MaxSamples); AsyncRealtimeAudioTask->StartBackgroundTask(); } // Not looping return false; } else if (BufferReadMode == EBufferReadMode::Synchronous) { return MixerBuffer->ReadCompressedData(SourceVoiceBuffers[BufferIndex]->AudioData.GetData(), WaveInstance->LoopingMode != LOOP_Never); } else { AUDIO_MIXER_CHECK_AUDIO_PLAT_THREAD(MixerDevice); check(!AsyncRealtimeAudioTask); AsyncRealtimeAudioTask = new FAsyncRealtimeAudioTask(MixerBuffer, SourceVoiceBuffers[BufferIndex]->AudioData.GetData(), WaveInstance->LoopingMode != LOOP_Never, BufferReadMode == EBufferReadMode::AsynchronousSkipFirstFrame); AsyncRealtimeAudioTask->StartBackgroundTask(); // Not looping return false; } }
bool USoundWave::IsReadyForFinishDestroy() { const bool bIsStreamingInProgress = IStreamingManager::Get().GetAudioStreamingManager().IsStreamingInProgress(this); // Wait till streaming and decompression finishes before deleting resource. if ( !bIsStreamingInProgress && (( AudioDecompressor == nullptr ) || AudioDecompressor->IsDone()) ) { if (ResourceState == ESoundWaveResourceState::NeedsFree) { DECLARE_CYCLE_STAT(TEXT("FAudioThreadTask.FreeResources"), STAT_AudioFreeResources, STATGROUP_AudioThreadCommands); USoundWave* SoundWave = this; ResourceState = ESoundWaveResourceState::Freeing; FAudioThread::RunCommandOnAudioThread([SoundWave]() { SoundWave->FreeResources(); }, GET_STATID(STAT_AudioFreeResources)); } } return ResourceState == ESoundWaveResourceState::Freed; }
UObject* UMP3SoundFactory::FactoryCreateBinary(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, FFeedbackContext* Warn) { FEditorDelegates::OnAssetPreImport.Broadcast(this, Class, InParent, Name, Type); if (mpg123_init == nullptr) { Warn->Logf(ELogVerbosity::Error, TEXT("Function pointer was null. Was %s found?"), DLL_NAME); FEditorDelegates::OnAssetPostImport.Broadcast(this, nullptr); return nullptr; } // if the sound already exists, remember the user settings USoundWave* ExistingSound = FindObject<USoundWave>(InParent, *Name.ToString()); // stop playing the file, if it already exists (e.g. reimport) TArray<UAudioComponent*> ComponentsToRestart; FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager(); if (AudioDeviceManager && ExistingSound) { AudioDeviceManager->StopSoundsUsingResource(ExistingSound, ComponentsToRestart); } // Read the mp3 header and make sure we have valid data UMP3Decoder Decoder(Warn); Decoder.Init(Buffer, BufferEnd); if (Decoder.BitsPerSample != 16) { Warn->Logf(ELogVerbosity::Error, TEXT("Unreal only supports 16bit WAVE data (%s)."), *Name.ToString()); FEditorDelegates::OnAssetPostImport.Broadcast(this, nullptr); return nullptr; } if (Decoder.Channels != 1 && Decoder.Channels != 2) { Warn->Logf(ELogVerbosity::Error, TEXT("Unreal only supports 1-2 channel WAVE data (Mono/Stereo). (%s)."), *Name.ToString()); FEditorDelegates::OnAssetPostImport.Broadcast(this, nullptr); return nullptr; } //on reimport, reuse settings, wipe data. otherwise create new. (UE4 WAVE import has some more checks, maybe implement, too?) USoundWave* Sound; if (ExistingSound && bMP3SoundFactoryIsReimport) { Sound = ExistingSound; Sound->FreeResources(); Sound->InvalidateCompressedData(); } else { Sound = NewObject<USoundWave>(InParent, Name, Flags); } Sound->AssetImportData->Update(GetCurrentFilename()); TArray<uint8> RawWavBuffer; RawWavBuffer.Reserve((BufferEnd - Buffer) * 16); //actual decoding Decoder.Decode(RawWavBuffer); Sound->RawData.Lock(LOCK_READ_WRITE); void* LockedData = Sound->RawData.Realloc(RawWavBuffer.Num() * RawWavBuffer.GetTypeSize()); FMemory::Memcpy(LockedData, RawWavBuffer.GetData(), RawWavBuffer.Num() * RawWavBuffer.GetTypeSize()); Sound->RawData.Unlock(); RawWavBuffer.Empty(); // Calculate duration. Sound->Duration = (float)Decoder.SizeInBytes / Decoder.Samplerate / Decoder.Channels / (BITS_PER_SAMPLE / 8); Sound->SampleRate = Decoder.Samplerate; Sound->NumChannels = Decoder.Channels; Sound->RawPCMDataSize = Decoder.SizeInBytes; FEditorDelegates::OnAssetPostImport.Broadcast(this, Sound); if (ExistingSound) { Sound->PostEditChange(); } for (int32 ComponentIndex = 0; ComponentIndex < ComponentsToRestart.Num(); ++ComponentIndex) { ComponentsToRestart[ComponentIndex]->Play(); } return Sound; }