Files
UnrealEngineUWP/Engine/Source/Runtime/HTML5/HTML5Audio/Private/HTML5AudioBuffer.cpp
Matthew Griffin 98a1cdce0f Added Structs to store streamed audio chunks for runtime streaming or in DDC.
Followed pattern set out by texture streaming so that each chunk of audio data resides in its own bulkdata struct. It is currently possible to split audio into chunks when SoundWaves are marked for streaming but there is no way of doing this exposed at present.
Changed the parameters of FAudioDevice::GetRuntimeFormat so that the relevant SoundWave must be passed in, to allow for different formats for individual sounds/streaming options.
USoundWave::FreeResources no longer resets the NumChannels as it is unnecessary and causes sounds to be unable to play after the OGG data is flushed when attempting to switch to OPUS.

[CL 2099012 by Matthew Griffin in Main branch]
2014-06-09 11:13:16 -04:00

201 lines
5.2 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "HTML5AudioDevice.h"
#include "AudioEffect.h"
#include "Engine.h"
#include "IAudioFormat.h"
#include "AudioDecompress.h"
/*------------------------------------------------------------------------------------
FALSoundBuffer.
------------------------------------------------------------------------------------*/
/**
* Constructor
*
* @param AudioDevice audio device this sound buffer is going to be attached to.
*/
FALSoundBuffer::FALSoundBuffer( FALAudioDevice* InAudioDevice )
{
AudioDevice = InAudioDevice;
}
/**
* Destructor
*
* Frees wave data and detaches itself from audio device.
*/
FALSoundBuffer::~FALSoundBuffer( void )
{
if( ResourceID )
{
AudioDevice->WaveBufferMap.Remove( ResourceID );
}
// Delete AL buffers.
alDeleteBuffers( 1, BufferIds );
}
/**
* Static function used to create a buffer.
*
* @param InWave USoundNodeWave to use as template and wave source
* @param AudioDevice audio device to attach created buffer to
* @param bIsPrecacheRequest Whether this request is for precaching or not
* @return FALSoundBuffer pointer if buffer creation succeeded, NULL otherwise
*/
FALSoundBuffer* FALSoundBuffer::Init( FALAudioDevice* AudioDevice ,USoundWave* InWave )
{
// Can't create a buffer without any source data
if (InWave == NULL || InWave->NumChannels == 0)
{
return NULL;
}
FALSoundBuffer *Buffer = NULL;
switch (static_cast<EDecompressionType>(InWave->DecompressionType))
{
case DTYPE_Setup:
// Has circumvented pre-cache mechanism - pre-cache now
AudioDevice->Precache(InWave, true, false);
// Recall this function with new decompression type
return Init(AudioDevice, InWave);
case DTYPE_Native:
if (InWave->ResourceID)
{
Buffer = static_cast<FALSoundBuffer*>(AudioDevice->WaveBufferMap.FindRef(InWave->ResourceID));
}
if (!Buffer)
{
Buffer = CreateNativeBuffer(AudioDevice, InWave);
}
break;
case DTYPE_Invalid:
case DTYPE_Preview:
case DTYPE_Procedural:
case DTYPE_RealTime:
default:
// Invalid will be set if the wave cannot be played
break;
}
return Buffer;
}
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 );
}
FWaveModInfo WaveInfo;
Wave->InitAudioResource(AudioDevice->GetRuntimeFormat(Wave));
FALSoundBuffer* Buffer = NULL;
// Find the existing buffer if any
if( Wave->ResourceID )
{
Buffer = AudioDevice->WaveBufferMap.FindRef( Wave->ResourceID );
}
if( Buffer == NULL )
{
// Create new buffer.
Buffer = new FALSoundBuffer( AudioDevice );
alGenBuffers( 1, Buffer->BufferIds );
AudioDevice->alError( TEXT( "RegisterSound" ) );
// Allocate new resource ID and assign to USoundNodeWave. A value of 0 (default) means not yet registered.
int ResourceID = AudioDevice->NextResourceID++;
Buffer->ResourceID = ResourceID;
Wave->ResourceID = ResourceID;
AudioDevice->Buffers.Add( Buffer );
AudioDevice->WaveBufferMap.FindOrAdd(ResourceID) = Buffer ;
// Keep track of associated resource name.
Buffer->ResourceName = Wave->GetPathName();
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;
}