基本的な AudioLink の実装は、IAudioLinkFactory の定義および登録を行う数行のコードで作成することができます。しかし、他の API の型を利用すると、実装をさらに構築するのに役立つ場合があります。
AudioLink の各 C++ の中核的な型の概要と、いくつかのコード例を以下に示します。
AudioLink ソース コードは、Engine\Source\Runtime\AudioLink\ にあります。
IAudioLinkFactory
すべての AudioLink の実装には、IAudioLinkFactory から派生したファクトリ クラスが必要です。このクラスには、複数の純粋な仮想関数が含まれており、実装のエントリ ポイントを定義します。
Null が返されるとアサーションが行われるため、各関数は実装する必要があります。Unreal Engine (UE) では、すべての呼び出しがスレッドセーフにディスパッチされ、理想的にはロックされることなく効率的に返されることを想定しています。
多数の個々のパラメータではなく、パラメータの構造体を利用し、こうした関数呼び出しの拡張を簡素化することができます。
登録できるのは、単一のファクトリ オブジェクトのみです。追加のファクトリは、起動時の致命的なエラーのアサーションを行います。
プラグインの実装
AudioLink は、IModularFeature インターフェースを利用して UE を拡張します。IAudioLinkFactory コンストラクタは登録を処理するため、ファクトリのインスタンスを作成すれば、その認識には十分です。実装の StartupModule() でこれを処理します。
UE のプラグインを実装するにはさまざまな方法がありますが、オーディオの拡張には IModularFeature インターフェースが最も一般的です。
例:サブミックス AudioLink の実装
// サブミックス オーディオ リンクの作成時に使用するパラメータ
struct FAudioLinkSubmixCreateArgs
{
TWeakObjectPtr<const USoundSubmix> Submix;
FAudioDevice* Device = nullptr;
TWeakObjectPtr<const UAudioLinkSettingsAbstract> Settings;
};
// サブミックス オーディオ リンクを作成します。
// @param AudioLink インスタンスの作成に使用される InCreateArgs 引数
// @return 新しく作成されたリンク インスタンス (正常に作成された場合)
virtual TUniquePtr<IAudioLink> CreateSubmixAudioLink(const FAudioLinkSubmixCreateArgs& InCreateArgs) = 0;
/**
* サブミックス オーディオ リンクの作成時に使用するパラメータ
*/
struct FAudioLinkSubmixCreateArgs
{
TWeakObjectPtr<const USoundSubmix> Submix;
FAudioDevice* Device = nullptr;
TWeakObjectPtr<const UAudioLinkSettingsAbstract> Settings;
};
/**
* サブミックス オーディオ リンクを作成します。
* @param AudioLink インスタンスの作成に使用される InCreateArgs 引数
* @return 新しく作成されたリンク インスタンス (正常に作成された場合)。
*/
virtual TUniquePtr<IAudioLink> CreateSubmixAudioLink(const FAudioLinkSubmixCreateArgs& InCreateArgs) = 0;
例:ファクトリの実装
class FAudioLinkExampleFactory : public IAudioLinkFactory
{
public:
FAudioLinkExampleFactory() = default;
virtual ~FAudioLinkExampleFactory() = default;
static FName GetFactoryNameStatic();
protected:
/** IAudioLinkFactory の開始*/
FName GetFactoryName() const override;
TSubclassOf<UAudioLinkSettingsAbstract> GetSettingsClass() const override;
TUniquePtr<IAudioLink> CreateSubmixAudioLink(const FAudioLinkSubmixCreateArgs&) override;
TUniquePtr<IAudioLink> CreateSourceAudioLink(const FAudioLinkSourceCreateArgs&) override;
FAudioLinkSourcePushedSharedPtr CreateSourcePushedAudioLink(const FAudioLinkSourcePushedCreateArgs&) override;
FAudioLinkSynchronizerSharedPtr CreateSynchronizerAudioLink() override;
/** IAudioLinkFactory の終了*/
};
UAudioLinkSettingsAbstract
すべての AudioLink の実装には、バッファ サイズ、ハンドシェイク、UAsset 参照など、データと関連付けられたリンクを作成するために UAudioLinkSettingsAbstract から派生した設定クラスが必要です。
例:設定の実装
UCLASS(config = Engine, defaultconfig)
class AUDIOLINKEXAMPLERUNTIME_API UAudioLinkSettingsExample : public UAudioLinkSettingsAbstract
{
GENERATED_BODY()
UPROPERTY(Config, EditAnywhere, Category = "Example|AudioLink")
float MyBufferSize = 1.0f;
};
class FAudioLinkSettingsProxyExample : public IAudioLinkSettingsProxy
{
public:
FAudioLinkSettingsProxyExample(const UAudioLinkSettingsExample&);
virtual ~FAudioLinkSettingsProxyExample() = default;
float GetMyBufferSize() const { return MyBufferSize; }
private:
#if WITH_EDITOR
void RefreshFromSettings(UAudioLinkSettingsAbstract* InSettings, FPropertyChangedEvent& InPropertyChangedEvent) override
{
MyBufferSize = CastChecked<UAudioLinkSettingsExample>(InSettings)->MyBufferSize;
}
#endif //WITH_EDITOR
float MyBufferSize = 0;
};
IAudioLinkSettingsProxy
AudioLink 設定は、プロキシの設計パターンに従うため、オーディオが通常実行されているゲーム スレッド外に安全に出ることができます。設定のスレッドセーフなコピーが作成され、設定の UObject に共有ポインタとしてアタッチされます。
このアプローチには、主に 2 つのメリットがあります。
- 共有ポインタを持つスレッドがまだ存在する場合は操作を安全に継続できるため、ガベージ コレクションに対する保護を提供します。
PostEditChangedPropertyは、エディタ内で変更が行われると、設定の動作が標準アセットのようになるように自動的にデータをプロキシに渡します。
すべての AudioLink 設定アセットは、自身のプロキシを実装し、所有するアセットの変更を RefreshFromSettings 関数を使用して実装する必要があります。
さらに、プロパティのデフォルト設定を実装する必要があります。これは、DefaultEngine.ini でシリアル化されます。「例:設定の実装」では、これは設定オブジェクトの defaultconfig マークアップを使用してアーカイブされます。
設定のデフォルトは、プロパティが設定されていない場合に使用されるため、AudioLink を作成するたびにパスする必要はありません。
UFactory の実装
Unreal Engine では、AudioLink 設定がアセットとしてシリアル化されます。そのため、アセットを作成できるように設定のクラスにアセット ファクトリを実装することは、ユーザーに任されています。
設定:標準設定のファクトリの実装
class FAssetTypeActions_AudioLinkExampleSettings : public FAssetTypeActions_Base
{
public:
virtual FText GetName() const override;
virtual FColor GetTypeColor() const override;
virtual const TArray<FText>& GetSubMenus() const override;
virtual UClass* GetSupportedClass() const override;
virtual uint32 GetCategories() override;
};
UCLASS(hidecategories = Object, MinimalAPI)
class UAudioLinkExampleSettingsFactory : public UFactory
{
GENERATED_UCLASS_BODY()
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
virtual uint32 GetMenuCategories() const override;
};
IAudioLink
IAudioLink は IAudioLinkFactory によって使用される主な抽象化であり、多くの場合、TUniquePtr によって返されます。これは、プラグイン固有の実装に対し、非表示の詳細を含めるために設計された不透明型で、コンシューマーとプロデューサー両方へのスレッドセーフな共有ポインタに含まれます。コンシューマー オブジェクトは、プロデューサーへの弱い参照を維持し、プロデューサーが削除されると接続を安全に終了します (通常は、存続期間が経過したためにリンクが終了する場合)。
例:インスタンスの実装
//* AudioLink インスタンス。存続期間の管理のために共有ポインタを保持するコンテナ。*/
struct FExampleLink : IAudioLink
{
// 新しいバッファをリッスンする循環バッファ (サブミックス/ソース)
FSharedBufferedOutputPtr ProducerSP;
// サンプルのクライアント
FSharedExampleAudioInputClientPtr ConsumerSP;
// ...
};
IAudioMixerPlatformInterface
UE を使用して AudioLink Factory を登録する場合、AudioLink Platform Mixer がインスタンス化され、プラットフォームのハードウェアではなく AudioLink に接続されます。従来、サードパーティのライブラリでは .ini ファイルを変更してこれを防ぐ必要がありましたが、AudioLink ではこの手間がかからないようになりました。
IBufferedAudioOutput
ほとんどの AudioLink インスタンスでは、IBufferedAudioOutput から派生したクラスを利用し、プロデューサー オブジェクトを作成します。これは内部では、パルス符号変調 (PCM) データの循環バッファとして機能し、コンシューマー オブジェクトによって取り込まれるのを待機します。
循環バッファは、先入れ先出し (FIFO) のデータ構造を持ち、UE 境界全体で PCM データをバッファします。AudioLink の場合、これらはアトミックですが、ロックフリーではありません。
このインターフェースの実装は、すでにソース コード内で定義されています (FBufferedSourceListener および FBufferedSubmixListener)。そのため、これらの型の派生バージョンを作成する必要はないでしょう。
多くの場合、入力される音声の形式が認識される前にリンクが作成されます。そのため、形式が認識されてから OnFormatKnown デリゲートがアクティベートされます。これが発生した場合は、デリゲートを設定し、再生を開始してください。
これらのオブジェクトに割り当てる領域は、慎重に検討してください。サイズが大きすぎるとレイテンシーにつながりますが、小さすぎるとバッファ アンダーランにつながります。通常は、コンシューマ-のビットレートに対して 2:1 以上の比率のサイズを使用します。
リンク インスタンスの存続期間
リンク インスタンスの存続期間は、そのタイプによって異なります。
- ソース リンクは、一般にソースの再生の有効期間中は存続します。終了すると、ソースは削除され、リンクが切断されます。この切断は、主にソースの形式がすべての新しいソースで異なる可能性があることによるものです。
- サブミックス リンクは、サブミックスの実行中に開かれます。これは、通常はアプリケーションのランタイム時です。これは、特にエディタにおいて、インスタンスの存続期間によって引き起こされる問題につながる可能性があります。詳細については、「トラブルシューティング」を参照してください。
IAudioLinkSynchronizer
すべての AudioLink の実装には、IAudioLinkSynchronizer から派生したシンクロナイザ クラスが必要です。これには、さまざまなコールバックを設定するための登録および削除の呼び出しが含まれます。このクラスは別のクロック ソースに同期するため、UE と外部アプリケーションの同期を維持できます。
これらの各コールバックには、次のようなスレッドセーフなデリゲートを提供する必要があります。
SuspendResumeOpenStreamCloseStreamBeginRenderEndRender
外部の AudioLink ファクトリの形式を UE に通知するには、OpenStream を使用します。この通知は、ブロック/サンプル レートとチャンネル数をマッチングするのに最も役立ちます。
外部のレンダラと UE を同期するには、BeginRender および EndRender を使用します。
例:シンクロナイザーの実装
struct FExampleSynchronizerAudioLink : IAudioLinkSynchronizer
{
IAudioLinkSynchronizer::FOnSuspend OnSuspend;
IAudioLinkSynchronizer::FOnResume OnResume;
IAudioLinkSynchronizer::FOnOpenStream OnOpenStream;
IAudioLinkSynchronizer::FOnCloseStream OnCloseStream;
IAudioLinkSynchronizer::FOnBeginRender OnBeginRender;
IAudioLinkSynchronizer::FOnEndRender OnEndRender;
FRWLock RwLock;
// ...
#define MAKE_DELEGATE_FUNC(X)\
FDelegateHandle Register##X##Delegate(const FOn##X::FDelegate& InDelegate) override\
{\
FWriteScopeLock WriteLock(RwLock);\
return On##X.Add(InDelegate);\
}\
bool Remove##X##Delegate(const FDelegateHandle& InHandle) override\
{\
FWriteScopeLock WriteLock(RwLock);\
return On##X.Remove(InHandle);\
}
MAKE_DELEGATE_FUNC(Suspend)
MAKE_DELEGATE_FUNC(Resume)
MAKE_DELEGATE_FUNC(OpenStream)
MAKE_DELEGATE_FUNC(CloseStream)
MAKE_DELEGATE_FUNC(BeginRender)
MAKE_DELEGATE_FUNC(EndRender)
#undef MAKE_DELEGATE_FUNC
};