Wwise SDKの大部分の機能を AkSoundEngine
クラスからUnityで利用できます。C++ ネームスペースの AK::SoundEngine
や AK::MusicEngine
などの代わりとしてとらえることができます。元のSDKと比較してAPIバインディングにどのような変更があったかは、 APIの制約事項 を参照してください。 複雑な状況の場合は、コードでWwiseファンクションをコールする必要があります。APIで、全てのファンクションの GameObjectID
がGameObjectのUnityフレーバーに置き換えられます。AkGameObj コンポーネントを既にマニュアル操作で追加してない限り、ランタイムにこのGameObjectに自動的に追加されます。
EventやBankの文字列の代わりに数値IDを使う
ネーティブWwise APIで、文字列やIDを使ってWwiseプロジェクト内のイベントやその他の名前の付いたオブジェクトをトリガーできます。C::の世界でも、 Wwise_IDs.h
ファイルを Wwise_IDs.cs
に変換することで、同様にできます。Assets > Wwise > Convert Wwise SoundBank IDsをクリックする。これが機能するには、Pythonをインストールしておく必要があります。
MIDIをWwiseに送る
MIDIをWwiseに送るには、 AkMIDIPostArray
クラスの AkMIDIPost
メンバーをfillして、以下の方式のどれかをコールします:
以下は、サウンドエンジンにMIDIメッセージを送るための基本的なスクリプトです。
public class MyMIDIBehaviour : UnityEngine.MonoBehaviour
{
private void Start()
{
AkMIDIPostArray MIDIPostArrayBuffer = new AkMIDIPostArray(6);
AkMIDIPost midiEvent = new AkMIDIPost();
midiEvent.byType = AkMIDIEventTypes.NOTE_ON;
midiEvent.byChan = 0;
midiEvent.byOnOffNote = 56;
midiEvent.byVelocity = 127;
midiEvent.uOffset = 0;
MIDIPostArrayBuffer[0] = midiEvent;
midiEvent.byOnOffNote = 60;
MIDIPostArrayBuffer[1] = midiEvent;
midiEvent.byOnOffNote = 64;
MIDIPostArrayBuffer[2] = midiEvent;
midiEvent.byType = AkMIDIEventTypes.NOTE_OFF;
midiEvent.byOnOffNote = 56;
midiEvent.byVelocity = 0;
midiEvent.uOffset = 48000 * 8;
MIDIPostArrayBuffer[3] = midiEvent;
midiEvent.byOnOffNote = 60;
MIDIPostArrayBuffer[4] = midiEvent;
midiEvent.byOnOffNote = 64;
MIDIPostArrayBuffer[5] = midiEvent;
SynthEvent.PostMIDI(gameObject, MIDIPostArrayBuffer);
}
}
Unityのオフラインレンダリング
Unityインテグレーションで、 オフラインレンダリング機能が Wwise SDK内 で公開されるので、オーディオサンプルの取得方法が簡素化されます。
AkSoundEngine.StartDeviceCapture
で、キャプチャする特定の出力オーディオデバイスをセットアップすることで、 AkSoundEngine.UpdateCaptureSampleCount
をコールして利用可能なサンプル数を確認することができ、 AkSoundEngine.GetCaptureSamples
をコールしてオーディオサンプルを取得できるようになります。
以下は、Unityインテグレーションを使い、Unityのスクリーンキャプチャ機能と合わせてオフラインオーディオレンダリングを実行する方法を示した例です。この機能を実装すれば、マルチプレックスのポストプロセスの処理を簡単に実行して、キャプチャしたオーディオサンプルと動画フレームを組み合わせてムービーにすることができます。
public abstract class WwiseOfflineRenderer : UnityEngine.MonoBehaviour
{
public bool IsOfflineRendering { get; set; }
public bool StartWithOfflineRenderingEnabled = false;
private bool IsCurrentlyOfflineRendering = false;
public float FrameRate = 25.0f;
protected ulong OutputDeviceId = 0;
public abstract string GetUniqueScreenshotFileName(int frameCount);
public abstract void ProcessAudioSamples(float[] buffer);
protected void Start()
{
OutputDeviceId = AkSoundEngine.GetOutputID(AkSoundEngine.AK_INVALID_UNIQUE_ID, 0);
if (StartWithOfflineRenderingEnabled)
{
IsOfflineRendering = true;
Update();
}
}
private void LogAudioFormatInfo()
{
var sampleRate = AkSoundEngine.GetSampleRate();
var channelConfig = new AkChannelConfig();
var audioSinkCapabilities = new Ak3DAudioSinkCapabilities();
AkSoundEngine.GetOutputDeviceConfiguration(OutputDeviceId, channelConfig, audioSinkCapabilities);
UnityEngine.Debug.LogFormat("Sample Rate: {0}, Channels: {1}", sampleRate, channelConfig.uNumChannels);
}
protected void Update()
{
if (IsOfflineRendering != IsCurrentlyOfflineRendering)
{
IsCurrentlyOfflineRendering = IsOfflineRendering;
if (IsOfflineRendering)
{
#if UNITY_EDITOR
AkSoundEngineController.Instance.DisableEditorLateUpdate();
#endif
LogAudioFormatInfo();
AkSoundEngine.ClearCaptureData();
AkSoundEngine.StartDeviceCapture(OutputDeviceId);
}
else
{
AkSoundEngine.StopDeviceCapture(OutputDeviceId);
#if UNITY_EDITOR
AkSoundEngineController.Instance.EnableEditorLateUpdate();
#endif
}
}
var frameTime = IsOfflineRendering && FrameRate != 0.0f ?1.0f / FrameRate : 0.0f;
UnityEngine.Time.captureDeltaTime = frameTime;
AkSoundEngine.SetOfflineRenderingFrameTime(frameTime);
AkSoundEngine.SetOfflineRendering(IsOfflineRendering);
if (!IsOfflineRendering)
return;
UnityEngine.ScreenCapture.CaptureScreenshot(GetUniqueScreenshotFileName(UnityEngine.Time.frameCount));
var sampleCount = AkSoundEngine.UpdateCaptureSampleCount(OutputDeviceId);
if (sampleCount <= 0)
return;
var buffer = new float[sampleCount];
var count = AkSoundEngine.GetCaptureSamples(OutputDeviceId, buffer, (uint)buffer.Length);
if (count <= 0)
return;
ProcessAudioSamples(buffer);
}
}
UnityでAudio Input Sourceプラグインを使う
Audio Input Sourceプラグインを、C::スクリプト経由で使えます。Wwise SDKドキュメンテーションの、オーディオ入力ソースプラグインを参照してください。
以下は、Audio Input Sourceプラグインにテストトーンを送るための基本的なスクリプトです。
public class MyAudioInputBehaviour : UnityEngine.MonoBehaviour
{
public uint SampleRate = 48000;
public uint NumberOfChannels = 1;
public uint SampleIndex = 0;
public uint Frequency = 880;
private bool IsPlaying = true;
bool AudioSamplesDelegate(uint playingID, uint channelIndex, float[] samples)
{
for (uint i = 0; i < samples.Length; ++i)
samples[i] = UnityEngine.Mathf.Sin(Frequency * 2 * UnityEngine.Mathf.PI * (i + SampleIndex) / SampleRate);
if (channelIndex == NumberOfChannels - 1)
SampleIndex = (uint)(SampleIndex + samples.Length) % SampleRate;
return IsPlaying;
}
void AudioFormatDelegate(uint playingID, AkAudioFormat audioFormat)
{
audioFormat.channelConfig.uNumChannels = NumberOfChannels;
audioFormat.uSampleRate = SampleRate;
}
private void Start()
{
AkAudioInputManager.PostAudioInputEvent(AudioInputEvent, gameObject, AudioSamplesDelegate, AudioFormatDelegate);
}
public void StopSound()
{
IsPlaying = false;
}
private void OnDestroy()
{
AudioInputEvent.Stop(gameObject);
}
}
ポジショニングのカスタム設定をUnityで適用する
デフォルトで、 AkGameObj
コンポーネントは特定のUnity gameObject
に添付されて、そのtransform(オフセットの設定も可)を使ってフルポジショニングを行います。ファーストパーソンシューティングゲームなど多くのゲームは、これで充分です。一方、オーディオリスナーを単純に1つのゲームオブジェクト、例えばUnityのメインカメラなどに添付するだけでは、多くのサードパーソンゲームなどにあるカスタム設定されたカメラ角度で、ポジショニングに対応するための2つの要素(距離減衰とスペーシャリゼーション)に対応するのが難しい場合があります。また、その他のカスタム設定ポジショニングをプレイヤーに提供するゲームも、考えられます。
このような使い方のために、 AkGameObj
コンポーネントクラスで、Unityユーザーはポジショニングをオーバーライドできます。GetPosition()、
GetForward()、
GetUpward()
という3つのバーチャル方式を使って、ユーザーは AkGameObj
からサブクラスを取り、そのサブクラスコンポーネントでUnity gameObjects
のポジショニングをいくつでもカスタマイズできます。
デフォルトの AkAudioListener
動作を、カスタムコンポーネントを使ってオーバーライドするシンプルな例を、ここに示します。Wwiseにサードパーソンのプロジェクトをインテグレートした状態で、既存の AkAudioListener
と、それに関連付けられた AkGameObj
を削除します。次に、以下のスクリプトをMainCameraオブジェクトに添付して、さらに AkAudioListener
を添付して、最後にオーディオリスナーのポジションが追うターゲットとして、Unity gameObject
(例えばプレイヤーのアバター)を指定します。これ以降、全てのエミッターの距離減衰を計算するのに、選択したターゲットのUnity gameObject
のポジションをリスナーポジション(画面上の距離リスナー)としてとらえる一方、全てのエミッターのオリエンテーションを決めるには、引き続きメインカメラのオリエンテーションをリスナーのオリエンテーションとしてとらえます(画面外のオリエンテーションリスナー)。
#if !(UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.
using UnityEngine;
using System;
using System.Collections.Generic;
[AddComponentMenu ("Wwise/AkGameObj3rdPersonCam")]
[ExecuteInEditMode]
public class AkGameObj3rdPersonCam :
AkGameObj
{
public Transform target;
{
return target.GetComponent<
AkGameObj> ().GetPosition ();
}
}
#endif // #if !(UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.