大多数 Wwise SDK 函数可以通过 AkSoundEngine
类在 Unity 中使用。将它想象为 C++ 命名空间 AK::SoundEngine、AK::MusicEngine
等等的替代。想了解与原始
SDK 相比,API 绑定中所做的更改,请参考 API 限制 。 对于更复杂的情况,您需要从代码中调用 Wwise 函数。在 API 中,所有函数中的 GameObjectID
被Unity 风格的 GameObject 所替代。在运行时,这个 GameObject 中将自动添加一个 AkGameObj 组件,除非您之前已经手动添加过了。
对于 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
成员赋值,并调用以下任一方法:
以下是一个将 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 中的离线渲染功能并简化了音频样本的检索方式。
![](/images/2023.1.10_8659/?source=Unity&id=Caution.gif) | 注意: 务必确保在启用离线渲染时仅从主线程中异步调用 AkSoundEngine.LoadBank 和 AkSoundEngine.UnloadBank API。有关详细信息,请参阅自由调度音频渲染任务章节。 |
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 源插件
您可以通过 C# 脚本来调用 Audio Input 源插件。请参阅 Audio Input Source Plug-in from the Wwise SDK documentation.
以下是一个将测试音发送到 Audio Input 源插件的简单脚本:
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;
}
.
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
,并使用其变换功能(可选偏置)进行全面定位。这对很多游戏(如第一人称射击游戏)来说基本上就够了。不过,对于采用自定义镜头角度的游戏(如各种第三人称游戏)来说,假如只是简单地将音频听者关联到单个游戏对象(如 Unity 中的主镜头),定位的距离衰减和空间化这两个方面可能会不好协调。其他游戏可能还要给玩家提供别的自定义定位体验。
为此,AkGameObj
组件类允许用户重写定位。通过 GetPosition()、
GetForward()
和 GetUpward()
这三种虚方法,用户可选择继承 AkGameObj
的子类,并使用这些子类组件来灵活地自定义任意数量 Unity gameObject
的定位。
这里有一个简单的示例,展示了如何使用自定义组件来重写默认的 AkAudioListener
行为。对这个整合了 Wwise 的第三人称游戏项目,可移除现有的 AkAudioListener
及其关联的 AkGameObj
。然后,将以下脚本关联到 MainCamera 对象,并关联 AkAudioListener
。最后,指定音频听者位置所要跟随的目标 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.