2021-10-09

2021SC@SDUSC

先看一下Audio包。

Audio包下,除开两个第三方的引用,第一个就是具体的Audio源码文件。

The audio service used for music and sound effects playback.

这个类负责音乐和音效的播放。

继承System.Object大类。

重写下列方法:

System.Object.ToString()

System.Object.Equals(System.Object)

System.Object.Equals(System.Object, System.Object)

System.Object.ReferenceEquals(System.Object, System.Object)

System.Object.GetHashCode()

System.Object.GetType()

System.Object.MemberwiseClone()

API_CLASS(Static) class FLAXENGINE_API Audio
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Audio);
    friend class AudioStreamingHandler;
    friend class AudioClip;

public:

    /// <summary>
    /// The audio listeners collection registered by the service.
    /// </summary>
    static Array<AudioListener*> Listeners;

    /// <summary>
    /// The audio sources collection registered by the service.
    /// </summary>
    static Array<AudioSource*> Sources;

    /// <summary>
    /// The all audio devices.
    /// </summary>
    API_FIELD(ReadOnly) static Array<AudioDevice> Devices;

    /// <summary>
    /// Event called when audio devices collection gets changed.
    /// </summary>
    API_EVENT() static Action DevicesChanged;

    /// <summary>
    /// Event called when the active audio device gets changed.
    /// </summary>
    API_EVENT() static Action ActiveDeviceChanged;

public:

    /// <summary>
    /// Gets the active device.
    /// </summary>
    /// <returns>The active device.</returns>
    API_PROPERTY() static AudioDevice* GetActiveDevice();

    /// <summary>
    /// Gets the index of the active device.
    /// </summary>
    /// <returns>The active device index.</returns>
    API_PROPERTY() static int32 GetActiveDeviceIndex();

    /// <summary>
    /// Sets the index of the active device.
    /// </summary>
    /// <param name="index">The index.</param>
    API_PROPERTY() static void SetActiveDeviceIndex(int32 index);

public:

    /// <summary>
    /// Gets the master volume applied to all the audio sources (normalized to range 0-1).
    /// </summary>
    /// <returns>The value</returns>
    API_PROPERTY() static float GetMasterVolume();

    /// <summary>
    /// Sets the master volume applied to all the audio sources (normalized to range 0-1).
    /// </summary>
    /// <param name="value">The value.</param>
    API_PROPERTY() static void SetMasterVolume(float value);

    /// <summary>
    /// Gets the actual master volume (including all side effects and mute effectors).
    /// </summary>
    /// <returns>The final audio volume applied to all the listeners.</returns>
    API_PROPERTY() static float GetVolume();

    /// <summary>
    /// Sets the doppler effect factor. Scale for source and listener velocities. Default is 1.
    /// </summary>
    /// <param name="value">The value.</param>
    API_PROPERTY() static void SetDopplerFactor(float value);

public:

    static void OnAddListener(AudioListener* listener);
    static void OnRemoveListener(AudioListener* listener);

    static void OnAddSource(AudioSource* source);
    static void OnRemoveSource(AudioSource* source);
};

Audio类下面有什么成员呢?

ActiveDevice

表示活动的设备,具体类型是AudioDevice。

ActivedeviceIndex

活动设备的索引,Int32。

Devices

所有音频设备。是一个AudioDevice的数组。

DopplerFactor

设置多普勒效果因子。类型是System.Single

Volume

主音量,包括所有音效。System.Single类型

包括什么触发器呢。

ActiveDeviceChanged

活动音频设备改变时触发

DevicesChanged

音频设备的集合改变时触发

上面这些就是Audio.h中的接口作用。

然后具体地来看Audio.cpp吧。

const Char* ToString(AudioFormat value)
{
    switch (value)
    {
    case AudioFormat::Raw:
        return TEXT("Raw");
    case AudioFormat::Vorbis:
        return TEXT("Vorbis");
    default:
        return TEXT("");
    }
}

直接先来一个重写toString。

判断一下音频的格式,然后分Raw和Vorbis返回对应的格式。那么什么叫Raw和Vorbis呢?

在Types.h里面都定义好了,raw就是直接输出,vorbis就是有损压缩。

Array<AudioListener*> Audio::Listeners;
Array<AudioSource*> Audio::Sources;
Array<AudioDevice> Audio::Devices;
Action Audio::DevicesChanged;
Action Audio::ActiveDeviceChanged;
AudioBackend* AudioBackend::Instance = nullptr;

给一堆成员留好了Array位子,有什么呢,监听器,源,设备,然后是两个事件,上面已经说过了。

然后有个AudioBackend,音频后端,不知道是什么,咱们等一下就知道了。

namespace
{
    float MasterVolume = 1.0f;
    float Volume = 1.0f;
    int32 ActiveDeviceIndex = -1;
    bool MuteOnFocusLoss = true;
}

命名空间里面放了这么些参数:

主音量,默认是1

音量,默认1

活动设备索引,默认-1,估计就是不存在

失去焦点时禁音,默认为true

class AudioService : public EngineService
{
public:

    AudioService()
        : EngineService(TEXT("Audio"), -50)
    {
    }

    bool Init() override;
    void Update() override;
    void Dispose() override;
};

然后声明了一个AudioService,继承自引擎服务。

AudioService AudioServiceInstance;

紧跟着就把它放进来了,生成一个音频服务实例。

namespace
{
    void OnEnginePause()
    {
        AudioBackend::SetVolume(0.0f);
    }

    void OnEngineUnpause()
    {
        AudioBackend::SetVolume(Volume);
    }
}

几个方法。干什么的?

引擎暂停时,调用音频后端的方法,把音量置零,很合理。

引擎恢复时,把音量置成volume,但是这个volume没有参数,相当于就是用的上面初始化默认的1.0f。

void AudioSettings::Apply()
{
    ::MuteOnFocusLoss = MuteOnFocusLoss;
}

把class里面的muteonfocusloss状态应用到AudioSetting里面去。

AudioDevice* Audio::GetActiveDevice()
{
    return &Devices[ActiveDeviceIndex];
}

int32 Audio::GetActiveDeviceIndex()
{
    return ActiveDeviceIndex;
}

Get音频设备。Get设备索引就返回int32,get设备本身就返回设备集合中的一个设备引用。

void Audio::SetActiveDeviceIndex(int32 index)
{
    index = Math::Clamp(index, -1, Devices.Count() - 1);
    if (ActiveDeviceIndex == index)
        return;

    ActiveDeviceIndex = index;

    AudioBackend::OnActiveDeviceChanged();

    ActiveDeviceChanged();
}

设置活动设备索引。

传入一个int32,然后用了一个clamp方法限定范围,如果index就是当前index,直接返回;

然后设置当前index,调用活动设备改变的事件。

float Audio::GetMasterVolume()
{
    return MasterVolume;
}

void Audio::SetMasterVolume(float value)
{
    MasterVolume = Math::Saturate(value);
}

float Audio::GetVolume()
{
    return Volume;
}

void Audio::SetDopplerFactor(float value)
{
    value = Math::Max(0.0f, value);
    AudioBackend::SetDopplerFactor(value);
}

一组常规的get、set。

void Audio::OnAddListener(AudioListener* listener)
{
    ASSERT(!Listeners.Contains(listener));

    if (Listeners.Count() >= AUDIO_MAX_LISTENERS)
    {
        LOG(Error, "Unsupported amount of the audio listeners!");
        return;
    }

    Listeners.Add(listener);
    AudioBackend::Listener::OnAdd(listener);
}

添加监听器的方法。

先用一个断言,保证即将添加的监听器在监听器列表内。

void Audio::OnRemoveListener(AudioListener* listener)
{
    if (!Listeners.Remove(listener))
    {
        AudioBackend::Listener::OnRemove(listener);
    }
}

移除监听器。

void Audio::OnAddSource(AudioSource* source)
{
    ASSERT(!Sources.Contains(source));

    Sources.Add(source);
    AudioBackend::Source::OnAdd(source);
}

void Audio::OnRemoveSource(AudioSource* source)
{
    if (!Sources.Remove(source))
    {
        AudioBackend::Source::OnRemove(source);
    }
}

一组添加与移除音频源。和监听器的方法类似。

bool AudioService::Init()
{
    PROFILE_CPU_NAMED("Audio.Init");
    const auto settings = AudioSettings::Get();
    const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio;

    // Pick a backend to use
    AudioBackend* backend = nullptr;
#if AUDIO_API_NONE
    if (mute)
    {
        backend = New<AudioBackendNone>();
    }
#endif
#if AUDIO_API_PS4
    if (!backend)
    {
        backend = New<AudioBackendPS4>();
    }
#endif
#if AUDIO_API_SWITCH
    if (!backend)
    {
        backend = New<AudioBackendSwitch>();
    }
#endif
#if AUDIO_API_OPENAL
    if (!backend)
    {
        backend = New<AudioBackendOAL>();
    }
#endif
#if AUDIO_API_XAUDIO2
   if (!backend)
   {
      backend = New<AudioBackendXAudio2>();
   }
#endif
#if AUDIO_API_NONE
    if (!backend)
    {
        backend = New<AudioBackendNone>();
    }
#else
    if (mute)
    {
        LOG(Warning, "Cannot use mute audio. Null Audio Backend not available on this platform.");
    }
#endif
    if (backend == nullptr)
    {
        LOG(Error, "Failed to create audio backend.");
        return true;
    }
    AudioBackend::Instance = backend;

    LOG(Info, "Audio system initialization... (backend: {0})", AudioBackend::Name());

    if (AudioBackend::Init())
    {
        LOG(Warning, "Failed to initialize audio backend.");
    }

    Engine::Pause.Bind(&OnEnginePause);
    Engine::Unpause.Bind(&OnEngineUnpause);

    return false;
}

调用AudioService中的初始化方法。

可以看出,我们的audio后端就是在这里new出来的。

根据不同的音频API,ps4、switch、openAL等等,new不同的音频后端。

然后打印很多对应的log信息。

void AudioService::Update()
{
    PROFILE_CPU_NAMED("Audio.Update");

    // Update the master volume
    float masterVolume = MasterVolume;
    if (MuteOnFocusLoss && !Engine::HasFocus)
    {
        // Mute audio if app has no user focus
        masterVolume = 0.0f;
    }
    if (Math::NotNearEqual(Volume, masterVolume))
    {
        Volume = masterVolume;
        AudioBackend::SetVolume(masterVolume);
    }

    AudioBackend::Update();
}

刷新音频服务。

如果失焦时禁音,并且失焦,那么禁音。

如果主音量和音量不同,那么将两者同步。

void AudioService::Dispose()
{
    ASSERT(Audio::Sources.IsEmpty() && Audio::Listeners.IsEmpty());

    // Cleanup
    Audio::Devices.Resize(0);
    if (AudioBackend::Instance)
    {
        AudioBackend::Dispose();
        Delete(AudioBackend::Instance);
        AudioBackend::Instance = nullptr;
    }
    ActiveDeviceIndex = -1;
}

销毁音频服务。

断言,确保此时没有音频源,也没有音频监听器。

音频设备列表大小改为0.

如果此时有后端,先销毁音频后端。

然后把音频设备索引置-1,和默认值一致。

Audio.cpp就完事了。

下面是AudioBackend.h

// Listener
virtual void Listener_OnAdd(AudioListener* listener) = 0;
virtual void Listener_OnRemove(AudioListener* listener) = 0;
virtual void Listener_VelocityChanged(AudioListener* listener) = 0;
virtual void Listener_TransformChanged(AudioListener* listener) = 0;

截一小段,可以看出来这是一个辅助的handler,有大量的虚函数。


virtual ~AudioBackend()
{
}

析构函数,什么都没做

class Listener
{
public:

    FORCE_INLINE static void OnAdd(AudioListener* listener)
    {
        Instance->Listener_OnAdd(listener);
    }

    FORCE_INLINE static void OnRemove(AudioListener* listener)
    {
        Instance->Listener_OnRemove(listener);
    }

    FORCE_INLINE static void VelocityChanged(AudioListener* listener)
    {
        Instance->Listener_VelocityChanged(listener);
    }

    FORCE_INLINE static void TransformChanged(AudioListener* listener)
    {
        Instance->Listener_TransformChanged(listener);
    }
};

具体的Listener类。

规定了添加、移除、修改的方法,全部单例模式强制内联。

然后我们看一下AudioClip.cpp。在看cpp文件之前,发现这个是带API介绍的。那么先跳转到AudioClip.h和对应文档。

Class AudioClip

Audio clip stores audio data in a compressed or uncompressed format using a binary asset. Clips can be provided to audio sources or other audio methods to be played.

音频剪辑使用二进制资源以压缩或未压缩格式存储音频数据。剪辑可以提供给音频源或其他音频方法来播放。

Constructors构造器:

AudioClip()

成员:

Format 获取音频data的格式,类型是AudioFormat

Info 获取音频的元数据,类型AudioDTAINfo

Is3D 判断音频是否是3D的,bool类型。

IsStreamable  判断音频是否使用数据流 bool类型

IsStreamingTaskActive 判断音频流是否异步 bool类型

Length 返回音频片段的长度,单位是秒  类型为system.Single

struct Header
{
    AudioFormat Format;
    AudioDataInfo Info;
    bool Is3D;
    bool Streamable;
    uint32 OriginalSize;
    uint32 ImportedSize;
    uint32 SamplesPerChunk[ASSET_FILE_DATA_CHUNKS]; // Amount of audio samples (for all channels) stored per asset chunk (uncompressed data samples count)
};

给音频clip加的一个头部结构。

可以看到包括了音频的信息,格式,3D属性等等。

bool AudioClip::StreamingTask::Run()
{
    AssetReference<AudioClip> ref = _asset.Get();
    if (ref == nullptr)
    {
        return true;
    }
    const auto& queue = ref->StreamingQueue;
    auto clip = ref.Get();

    // Update the buffers
    for (int32 i = 0; i < queue.Count(); i++)
    {
        const auto idx = queue[i];
        uint32& bufferId = clip->Buffers[idx];

        if (bufferId == AUDIO_BUFFER_ID_INVALID)
        {
            AudioBackend::Buffer::Create(bufferId);
        }
        else
        {
            // Release unused data
            AudioBackend::Buffer::Delete(bufferId);
            bufferId = 0;
        }
    }

    // Load missing buffers data
    const auto format = clip->Format();
    AudioDataInfo info = clip->AudioHeader.Info;
    const uint32 bytesPerSample = info.BitDepth / 8;
    for (int32 i = 0; i < queue.Count(); i++)
    {
        const auto idx = queue[i];
        const uint32 bufferId = clip->Buffers[idx];
        if (bufferId == AUDIO_BUFFER_ID_INVALID)
            continue;

        byte* data;
        uint32 dataSize;
        Array<byte> outTmp;

        const auto chunk = clip->GetChunk(idx);
        if (chunk == nullptr || chunk->IsMissing())
        {
            LOG(Warning, "Missing audio streaming data chunk.");
            return true;
        }

        // Get raw data or decompress it
        switch (format)
        {
        case AudioFormat::Vorbis:
        {
#if COMPILE_WITH_OGG_VORBIS
            OggVorbisDecoder decoder;
            MemoryReadStream stream(chunk->Get(), chunk->Size());
            AudioDataInfo outInfo;
            if (decoder.Convert(&stream, outInfo, outTmp))
            {
                LOG(Warning, "Audio data decode failed (OggVorbisDecoder).");
                return true;
            }
            // TODO: validate decompressed data header info?
            data = outTmp.Get();
            dataSize = outTmp.Count();
#else
         LOG(Warning, "OggVorbisDecoder is disabled.");
         return true;
#endif
        }
        break;
        case AudioFormat::Raw:
        {
            data = chunk->Get();
            dataSize = chunk->Size();
        }
        break;
        default:
        CRASH;
            return true;
        }

        // Write samples to the audio buffer
        info.NumSamples = dataSize / bytesPerSample;
        AudioBackend::Buffer::Write(bufferId, data, info);
    }

    // Update the sources
    for (int32 sourceIndex = 0; sourceIndex < Audio::Sources.Count(); sourceIndex++)
    {
        // TODO: collect refs to audio clip from sources and use faster iteration (but do it thread-safe)

        const auto src = Audio::Sources[sourceIndex];
        if (src->Clip == clip && src->GetState() == AudioSource::States::Playing)
        {
            src->RequestStreamingBuffersUpdate();
        }
    }

    return false;
}

实现Run()方法。

如果最后运行成功了,返回false;如果中间哪里失败了,就发一条log,并且返回true。

void AudioClip::StreamingTask::OnEnd()
{
    // Unlink
    if (_asset)
    {
        ASSERT(_asset->_streamingTask == this);
        _asset->_streamingTask = nullptr;
        _asset = nullptr;
    }
    _dataLock.Release();

    // Base
    ThreadPoolTask::OnEnd();
}

流任务结束,Onend()时:

首先把asset相关的指针该销毁的销毁,该置为空的置为空;

然后触发线程池的Onend方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值