大多数 Wwise SDK 函数可以通过 AkSoundEngine
类在 Unity 中使用。将它想象为 C++ 命名空间 AK::SoundEngine、AK::MusicEngine
等等的替代。See
API Limitations for changes made in the API binding compared to the original SDK. 对于更复杂的情况,您需要从代码中调用 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 中的离线渲染功能并简化了音频样本的检索方式。
| 注意: 务必确保在启用离线渲染时仅从主线程中异步调用 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.