Пример #1
1
/**
 * Static function used to create an OpenAL buffer and upload raw PCM data to.
 *
 * @param InWave		USoundWave to use as template and wave source
 * @param AudioDevice	audio device to attach created buffer to
 * @return FXAudio2SoundBuffer pointer if buffer creation succeeded, NULL otherwise
 */
FXAudio2SoundBuffer* FXAudio2SoundBuffer::CreatePreviewBuffer( FXAudio2Device* XAudio2Device, USoundWave* Wave, FXAudio2SoundBuffer* Buffer )
{
	FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager();
	check(AudioDeviceManager != nullptr);

	if (Buffer)
	{
		AudioDeviceManager->FreeBufferResource(Buffer);
	}

	// Create new buffer.
	Buffer = new FXAudio2SoundBuffer( XAudio2Device, SoundFormat_PCMPreview );

	// Take ownership the PCM data
	Buffer->PCM.PCMData = Wave->RawPCMData;
	Buffer->PCM.PCMDataSize = Wave->RawPCMDataSize;

	Wave->RawPCMData = NULL;

	// Copy over whether this data should be freed on delete
	Buffer->bDynamicResource = Wave->bDynamicResource;

	Buffer->InitWaveFormatEx( WAVE_FORMAT_PCM, Wave, true );

	AudioDeviceManager->TrackResource(Wave, Buffer);

	return( Buffer );
}
Пример #2
1
/**
 * Static function used to create an OpenAL buffer and upload decompressed ogg vorbis data to.
 *
 * @param InWave		USoundWave to use as template and wave source
 * @param AudioDevice	audio device to attach created buffer to
 * @return FXAudio2SoundBuffer pointer if buffer creation succeeded, NULL otherwise
 */
FXAudio2SoundBuffer* FXAudio2SoundBuffer::CreateNativeBuffer( FXAudio2Device* XAudio2Device, USoundWave* Wave )
{
	// Check to see if thread has finished decompressing on the other thread
	if( Wave->AudioDecompressor != NULL )
	{
		Wave->AudioDecompressor->EnsureCompletion();

		// Remove the decompressor
		delete Wave->AudioDecompressor;
		Wave->AudioDecompressor = NULL;
	}

	// Create new buffer.
	FXAudio2SoundBuffer* Buffer = new FXAudio2SoundBuffer( XAudio2Device, SoundFormat_PCM );

	// Take ownership the PCM data
	Buffer->PCM.PCMData = Wave->RawPCMData;
	Buffer->PCM.PCMDataSize = Wave->RawPCMDataSize;

	Wave->RawPCMData = NULL;

	// Keep track of associated resource name.
	Buffer->InitWaveFormatEx( WAVE_FORMAT_PCM, Wave, true );

	FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager();
	check(AudioDeviceManager != nullptr);
	AudioDeviceManager->TrackResource(Wave, Buffer);

	Wave->RemoveAudioResource();

	return( Buffer );
}
Пример #3
1
/**
 * Static function used to create an OpenAL buffer and upload raw PCM data to.
 *
 * @param InWave		USoundWave to use as template and wave source
 * @param AudioDevice	audio device to attach created buffer to
 * @return FCoreAudioSoundBuffer pointer if buffer creation succeeded, NULL otherwise
 */
FCoreAudioSoundBuffer* FCoreAudioSoundBuffer::CreatePreviewBuffer( FCoreAudioDevice* CoreAudioDevice, USoundWave* Wave, FCoreAudioSoundBuffer* Buffer )
{
    FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager();
    check(AudioDeviceManager != nullptr);
    
	if (Buffer)
	{
		AudioDeviceManager->FreeBufferResource( Buffer );
	}
	
	// Create new buffer.
	Buffer = new FCoreAudioSoundBuffer( CoreAudioDevice, SoundFormat_PCMPreview );
	
	// Take ownership the PCM data
	Buffer->PCMData = Wave->RawPCMData;
	Buffer->PCMDataSize = Wave->RawPCMDataSize;
	
	Wave->RawPCMData = NULL;
	
	// Copy over whether this data should be freed on delete
	Buffer->bDynamicResource = Wave->bDynamicResource;
	
	Buffer->InitAudioStreamBasicDescription( kAudioFormatLinearPCM, Wave, true );
	
	AudioDeviceManager->TrackResource( Wave, Buffer );
	
	return( Buffer );
}
Пример #4
1
void USoundClass::PostLoad()
{
	Super::PostLoad();

	for (int32 ChildIndex = ChildClasses.Num()-1; ChildIndex >= 0; ChildIndex--)
	{
		if (ChildClasses[ChildIndex] != NULL && ChildClasses[ChildIndex]->GetLinkerUE4Version() < VER_UE4_SOUND_CLASS_GRAPH_EDITOR)
		{
			// first come, first served
			if (ChildClasses[ChildIndex]->ParentClass == nullptr)
			{
				ChildClasses[ChildIndex]->ParentClass = this;
			}
			// if already set, we can't be a parent of this child
			else if (ChildClasses[ChildIndex]->ParentClass != this)
			{
				UE_LOG(LogAudio, Warning, TEXT("SoundClass '%s' - '%s' removed from children as '%s' is its parent."), *GetName(), *ChildClasses[ChildIndex]->GetName(), *ChildClasses[ChildIndex]->ParentClass->GetName());
				ChildClasses.RemoveAt(ChildIndex);
			}
		}
	}
	// Use the main/default audio device for storing and retrieving sound class properties
	FAudioDeviceManager* AudioDeviceManager = (GEngine ? GEngine->GetAudioDeviceManager() : nullptr);

	// Force the properties to be initialized for this SoundClass on all active audio devices
	if (AudioDeviceManager)
	{
		AudioDeviceManager->RegisterSoundClass(this);
	}
}
FIOSAudioSoundBuffer* FIOSAudioSoundBuffer::CreateNativeBuffer(FIOSAudioDevice* IOSAudioDevice, USoundWave* InWave)
{
    FWaveModInfo WaveInfo;

    InWave->InitAudioResource(IOSAudioDevice->GetRuntimeFormat(InWave));
    if (!InWave->ResourceData || InWave->ResourceSize <= 0 || !WaveInfo.ReadWaveInfo(InWave->ResourceData, InWave->ResourceSize))
    {
        InWave->RemoveAudioResource();
        return NULL;
    }

    uint32 UncompressedBlockSize = 0;
    uint32 CompressedBlockSize = 0;
    const uint32 PreambleSize = 7;
    const uint32 BlockSize = *WaveInfo.pBlockAlign;

    switch (*WaveInfo.pFormatTag)
    {
    case SoundFormat_ADPCM:
        // (BlockSize - PreambleSize) * 2 (samples per byte) + 2 (preamble samples)
        UncompressedBlockSize = (2 + (BlockSize - PreambleSize) * 2) * sizeof(int16);
        CompressedBlockSize = BlockSize;

        if ((WaveInfo.SampleDataSize % CompressedBlockSize) != 0)
        {
            InWave->RemoveAudioResource();
            return NULL;
        }
        break;

    case SoundFormat_LPCM:
        break;
    }

    // Create new buffer
    FIOSAudioSoundBuffer* Buffer = new FIOSAudioSoundBuffer(IOSAudioDevice, static_cast<ESoundFormat>(*WaveInfo.pFormatTag));

    Buffer->NumChannels = InWave->NumChannels;
    Buffer->SampleRate = InWave->SampleRate;
    Buffer->UncompressedBlockSize = UncompressedBlockSize;
    Buffer->CompressedBlockSize = CompressedBlockSize;
    Buffer->BufferSize = WaveInfo.SampleDataSize;

    Buffer->SampleData = static_cast<int16*>(FMemory::Malloc(Buffer->BufferSize));
    FMemory::Memcpy(Buffer->SampleData, WaveInfo.SampleDataStart, Buffer->BufferSize);

    FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager();
    check(AudioDeviceManager != nullptr);

    AudioDeviceManager->TrackResource(InWave, Buffer);
    InWave->RemoveAudioResource();

    return Buffer;
}
Пример #6
1
void USoundWave::FreeResources()
{
	check(IsInAudioThread());

	// Housekeeping of stats
	DEC_FLOAT_STAT_BY( STAT_AudioBufferTime, Duration );
	DEC_FLOAT_STAT_BY( STAT_AudioBufferTimeChannels, NumChannels * Duration );

	// GEngine is NULL during script compilation and GEngine->Client and its audio device might be
	// destroyed first during the exit purge.
	if( GEngine && !GExitPurge )
	{
		// Notify the audio device to free the bulk data associated with this wave.
		FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager();
		if (AudioDeviceManager)
		{
			AudioDeviceManager->StopSoundsUsingResource(this);
			AudioDeviceManager->FreeResource(this);
		}
	}

	if (CachedRealtimeFirstBuffer)
	{
		FMemory::Free(CachedRealtimeFirstBuffer);
		CachedRealtimeFirstBuffer = nullptr;
	}

	// Just in case the data was created but never uploaded
	if (RawPCMData)
	{
		FMemory::Free(RawPCMData);
		RawPCMData = nullptr;
	}

	// Remove the compressed copy of the data
	RemoveAudioResource();

	// Stat housekeeping
	DEC_DWORD_STAT_BY(STAT_AudioMemorySize, TrackedMemoryUsage);
	DEC_DWORD_STAT_BY(STAT_AudioMemory, TrackedMemoryUsage);
	TrackedMemoryUsage = 0;

	ResourceID = 0;
	bDynamicResource = false;
	DecompressionType = DTYPE_Setup;
	bDecompressedFromOgg = 0;

	USoundWave* SoundWave = this;
	FAudioThread::RunCommandOnGameThread([SoundWave]()
	{
		SoundWave->ResourceState = ESoundWaveResourceState::Freed;
	}, TStatId());
}
Пример #7
0
void USoundMix::BeginDestroy()
{
	if (!GExitPurge && GEngine)
	{
		FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager();
		if (AudioDeviceManager)
		{
			AudioDeviceManager->RemoveSoundMix(this);
		}
	}

	Super::BeginDestroy();
}
void FAudioDeviceManager::SetDebugSoloSoundCue(const TCHAR* SoundCue)
{
	if (!IsInAudioThread())
	{
		FAudioDeviceManager* AudioDeviceManager = this;
		FAudioThread::RunCommandOnAudioThread([AudioDeviceManager, SoundCue]()
		{
			AudioDeviceManager->SetDebugSoloSoundCue(SoundCue);

		});
		return;
	}

	DebugNames.DebugSoloSoundCue = SoundCue;
}
void FAudioDeviceManager::ToggleVisualize3dDebug()
{
	if (!IsInAudioThread())
	{
		FAudioDeviceManager* AudioDeviceManager = this;
		FAudioThread::RunCommandOnAudioThread([AudioDeviceManager]()
		{
			AudioDeviceManager->ToggleVisualize3dDebug();

		});

		return;
	}

	bVisualize3dDebug = !bVisualize3dDebug;
}
void FAudioDeviceManager::TogglePlayAllDeviceAudio()
{
	if (!IsInAudioThread())
	{
		FAudioDeviceManager* AudioDeviceManager = this;
		FAudioThread::RunCommandOnAudioThread([AudioDeviceManager]()
		{
			AudioDeviceManager->TogglePlayAllDeviceAudio();

		});

		return;
	}
	
	bPlayAllDeviceAudio = !bPlayAllDeviceAudio;
}
Пример #11
0
void FSoundSource::DrawDebugInfo()
{
	// Draw 3d Debug information about this source, if enabled
	FAudioDeviceManager* DeviceManager = GEngine->GetAudioDeviceManager();

	if (DeviceManager && DeviceManager->IsVisualizeDebug3dEnabled())
	{
		const uint32 AudioComponentID = WaveInstance->ActiveSound->GetAudioComponentID();

		if (AudioComponentID > 0)
		{
			DECLARE_CYCLE_STAT(TEXT("FAudioThreadTask.DrawSourceDebugInfo"), STAT_AudioDrawSourceDebugInfo, STATGROUP_TaskGraphTasks);

			USoundBase* Sound = WaveInstance->ActiveSound->GetSound();
			const FVector Location = WaveInstance->Location;

			const bool bSpatialized = Buffer->NumChannels == 2 && WaveInstance->bUseSpatialization;
			const FVector LeftChannelSourceLoc = LeftChannelSourceLocation;
			const FVector RightChannelSourceLoc = RightChannelSourceLocation;

			FAudioThread::RunCommandOnGameThread([AudioComponentID, Sound, bSpatialized, Location, LeftChannelSourceLoc, RightChannelSourceLoc]()
			{
				UAudioComponent* AudioComponent = UAudioComponent::GetAudioComponentFromID(AudioComponentID);
				if (AudioComponent)
				{
					UWorld* SoundWorld = AudioComponent->GetWorld();
					if (SoundWorld)
					{
						FRotator SoundRotation = AudioComponent->GetComponentRotation();
						DrawDebugCrosshairs(SoundWorld, Location, SoundRotation, 20.0f, FColor::White, false, -1.0f, SDPG_Foreground);

						if (bSpatialized)
						{
							DrawDebugCrosshairs(SoundWorld, LeftChannelSourceLoc, SoundRotation, 20.0f, FColor::Red, false, -1.0f, SDPG_Foreground);
							DrawDebugCrosshairs(SoundWorld, RightChannelSourceLoc, SoundRotation, 20.0f, FColor::Green, false, -1.0f, SDPG_Foreground);
						}

						const FString Name = Sound->GetName();
						DrawDebugString(SoundWorld, AudioComponent->GetComponentLocation() + FVector(0, 0, 32), *Name, nullptr, FColor::White, 0.033, false);
					}
				}
			}, GET_STATID(STAT_AudioDrawSourceDebugInfo));
		}
	}
}
void FAudioDeviceManager::RemoveSoundMix(USoundMix* SoundMix)
{
	if (!IsInAudioThread())
	{
		DECLARE_CYCLE_STAT(TEXT("FAudioThreadTask.RemoveSoundMix"), STAT_AudioRemoveSoundMix, STATGROUP_AudioThreadCommands);

		FAudioDeviceManager* AudioDeviceManager = this;
		FAudioThread::RunCommandOnAudioThread([AudioDeviceManager, SoundMix]()
		{
			AudioDeviceManager->RemoveSoundMix(SoundMix);

		}, GET_STATID(STAT_AudioRemoveSoundMix));

		return;
	}

	for (FAudioDevice* AudioDevice : Devices)
	{
		if (AudioDevice)
		{
			AudioDevice->RemoveSoundMix(SoundMix);
		}
	}
}
Пример #13
0
/**
 * Static function used to create a buffer.
 *
 * @param InWave USoundWave to use as template and wave source
 * @param AudioDevice audio device to attach created buffer to
 * @return FXAudio2SoundBuffer pointer if buffer creation succeeded, NULL otherwise
 */
FXAudio2SoundBuffer* FXAudio2SoundBuffer::Init( FAudioDevice* AudioDevice, USoundWave* Wave, bool bForceRealTime )
{
	// Can't create a buffer without any source data
	if( Wave == NULL || Wave->NumChannels == 0 )
	{
		return( NULL );
	}

	FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager();

	FXAudio2Device* XAudio2Device = ( FXAudio2Device* )AudioDevice;
	FXAudio2SoundBuffer* Buffer = NULL;

	// Allow the precache to happen if necessary
	EDecompressionType DecompressionType = Wave->DecompressionType;
	if (bForceRealTime && DecompressionType != DTYPE_Setup && DecompressionType != DTYPE_Streaming)
	{
		DecompressionType = DTYPE_RealTime;
	}

	switch( DecompressionType )
	{
	case DTYPE_Setup:
		// Has circumvented precache mechanism - precache now
		AudioDevice->Precache(Wave, true, false);

		// if it didn't change, we will recurse forever
		check(Wave->DecompressionType != DTYPE_Setup);

		// Recall this function with new decompression type
		return( Init( AudioDevice, Wave, bForceRealTime ) );

	case DTYPE_Preview:
		// Find the existing buffer if any
		if( Wave->ResourceID )
		{
			Buffer = (FXAudio2SoundBuffer*)AudioDeviceManager->GetSoundBufferForResourceID(Wave->ResourceID);
		}

		// Override with any new PCM data even if some already exists. 
		if( Wave->RawPCMData )
		{
			// Upload the preview PCM data to it
			Buffer = CreatePreviewBuffer( XAudio2Device, Wave, Buffer );
		}
		break;

	case DTYPE_Procedural:
		// Always create a new buffer for streaming procedural data
		Buffer = CreateProceduralBuffer( XAudio2Device, Wave );
		break;

	case DTYPE_RealTime:
		// Always create a new buffer for streaming ogg vorbis data
		Buffer = CreateQueuedBuffer( XAudio2Device, Wave );
		break;

	case DTYPE_Native:
	case DTYPE_Xenon:
		// Upload entire wav to XAudio2
		if( Wave->ResourceID )
		{
			Buffer = (FXAudio2SoundBuffer*)AudioDeviceManager->GetSoundBufferForResourceID(Wave->ResourceID);
		}

		if( Buffer == NULL )
		{
			Buffer = CreateNativeBuffer( XAudio2Device, Wave );
		}
		break;

	case DTYPE_Streaming:
		// Always create a new buffer for streaming sounds
		Buffer = CreateStreamingBuffer( XAudio2Device, Wave );
		break;

	case DTYPE_Invalid:
	default:
		// Invalid will be set if the wave cannot be played
		break;
	}

	return( Buffer );
}
Пример #14
0
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;
}
Пример #15
0
FALSoundBuffer* FALSoundBuffer::CreateNativeBuffer( FALAudioDevice* AudioDevice, USoundWave* Wave)
{
	SCOPE_CYCLE_COUNTER( STAT_AudioResourceCreationTime );

	// This code is not relevant for now on HTML5 but adding this for consistency with other platforms.
	// Check to see if thread has finished decompressing on the other thread

	if (Wave->AudioDecompressor != NULL)
	{
		Wave->AudioDecompressor->EnsureCompletion();

		// Remove the decompressor
		delete Wave->AudioDecompressor;
		Wave->AudioDecompressor = NULL;
	}

	// Can't create a buffer without any source data
	if( Wave == NULL || Wave->NumChannels == 0 )
	{
		return( NULL );
	}

	Wave->InitAudioResource(AudioDevice->GetRuntimeFormat(Wave));

	FALSoundBuffer* Buffer = NULL;
	FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager();
	check(AudioDeviceManager != nullptr);

	// Find the existing buffer if any
	if( Wave->ResourceID )
	{
		Buffer = static_cast<FALSoundBuffer*>(AudioDeviceManager->WaveBufferMap.FindRef(Wave->ResourceID));
	}

	if( Buffer == NULL )
	{
		// Create new buffer.
		Buffer = new FALSoundBuffer( AudioDevice );

		alGenBuffers( 1, Buffer->BufferIds );

		AudioDevice->alError( TEXT( "RegisterSound" ) );

		AudioDeviceManager->TrackResource(Wave, Buffer);

		Buffer->InternalFormat = AudioDevice->GetInternalFormat( Wave->NumChannels );
		Buffer->NumChannels = Wave->NumChannels;
		Buffer->SampleRate = Wave->SampleRate;

		if (Wave->RawPCMData)
		{
			// upload it
			Buffer->BufferSize = Wave->RawPCMDataSize;
			alBufferData( Buffer->BufferIds[0], Buffer->InternalFormat, Wave->RawPCMData, Wave->RawPCMDataSize, Buffer->SampleRate );

			// Free up the data if necessary
			if( Wave->bDynamicResource )
			{
				FMemory::Free( Wave->RawPCMData );
				Wave->RawPCMData = NULL;
				Wave->bDynamicResource = false;
			}
		}
		else
		{
			// get the raw data
			uint8* SoundData = ( uint8* )Wave->RawData.Lock( LOCK_READ_ONLY );
			// it's (possibly) a pointer to a wave file, so skip over the header
			int SoundDataSize = Wave->RawData.GetBulkDataSize();

			// is there a wave header?
			FWaveModInfo WaveInfo;
			if (WaveInfo.ReadWaveInfo(SoundData, SoundDataSize))
			{
				// if so, modify the location and size of the sound data based on header
				SoundData = WaveInfo.SampleDataStart;
				SoundDataSize = WaveInfo.SampleDataSize;
			}
			// let the Buffer know the final size
			Buffer->BufferSize = SoundDataSize;

			// upload it
			alBufferData( Buffer->BufferIds[0], Buffer->InternalFormat, SoundData, Buffer->BufferSize, Buffer->SampleRate );
			// unload it
			Wave->RawData.Unlock();
		}

		if( AudioDevice->alError( TEXT( "RegisterSound (buffer data)" ) ) || ( Buffer->BufferSize == 0 ) )
		{
			Buffer->InternalFormat = 0;
		}

		if( Buffer->InternalFormat == 0 )
		{
			UE_LOG ( LogAudio, Log,TEXT( "Audio: sound format not supported for '%s' (%d)" ), *Wave->GetName(), Wave->NumChannels );
			delete Buffer;
			Buffer = NULL;
		}
	}

	return Buffer;
}