unity Audio plugin

Native Audio Plugin SDK

Thisdocument describes the built-in native audio plugin interface of unity 5.0. Wewill do this by looking at some specific example plugins that grow incomplexity as we move along. This way, we start out with very basic conceptsand introduce more complex use-cases near the end of the document.

本文档介绍unity 5.0中内建的音频插件接口,通过一些的插件例程并不断提高难度的方式进行讲解。这样,我们会从基本概念的开始而到文档结尾部分会涉及到复杂的用例。

Download

Firstthing you need to do is to download the newest audio plugin SDK from here.

下载

首先要做的是:从这儿(https://bitbucket.org/Unity-Technologies/nativeaudioplugins)下载最新的插件SDK。

Overview

概览

Thenative audio plugin system consists of two parts:

内建的音频插件系统包括两个部分:

The native DSP (Digital     Signal Processing) plugin which has to be implemented as a .dll (Windows)     or .dylib (OSX) in C or C++. Unlike scripts and because of the high     demands on performance this has to be compiled for any platform that you     want to support, possibly with platform-specific optimizations.

The GUI which is developed     in C#. Note that the GUI is optional, so you always start out plugin     development by creating the basic native DSP plugin, and let Unity show a     default slider-based UI for the parameter descriptions that the native     plugin exposes. We recommend this approach to bootstrap any project.

使用C或C++语言编写.dll文件或.dylib(OSX)文件实现原生的DSP(数字信号处理)插件。和脚本不同,DSP对性能要求很高,这就得在希望支持的平台上进行编译,甚至还包括对特定平台的优化。

使用C#开发的GUI界面。GUI是可选的,而原生DSP插件开发是必不可少的,此外,Unity会为原生插件中的公开参数生成缺省的滑杆条控制,因此,我们建议以DSP作为工程开始。

Notethat you can initially prototype the C# GUI as a .cs file that you just dropinto the Assets/Editor folder (just like any other editor script). Later on youcan move this into a proper MonoDevelop project as your code starts to grow andneed better modularization and better IDE support. This enables you to compileit into a .dll, making it easier for the user to drop into the project and alsoin order to protect your code.

注意,可以将C#的GUI原型文件保存为.cs文件放到Assets/Editor文件夹下(就像其他编辑器脚本一样),后面随着代码增长,需要模块化和IDE支持时你可以使用MonoDevelop项目管理它,这样,你就能将其编译成一个.dll文件,让用户在需要时将其导入工程中就可以使用,同时还能起到保护代码的作用。

Alsonote that both the native DSP and GUI DLLs can contain multiple plugins andthat the binding happens only through the names of the effects in the pluginsregardless of what the DLL file is called.

另外,原生的DSP和GUI DLL中都可以包含多个插件,调用时的动态绑定仅通过插件中的效果名称进行而不必在意调用了哪个DLL。

What are all these files?

SDK中文件用途?

Thenative side of the plugin SDK actually only consists of one file(AudioPluginInterface.h), but to make it easy to have multiple plugin effectswithin the same DLL we have added supporting code to handle the effectdefinition and parameter registration in a simple unified way(AudioPluginUtil.h and AudioPluginUtil.cpp). Note that the NativePluginDemoproject contains a number of example plugins to get you started and show avariety of different plugin types that are useful in a game context. We placethis code in the public domain, so feel free to use this code as a startingpoint for your own creations.

原生插件的SDK只有一个文件(AudioPluginInterface.h),但为了在同一DLL中包含多个插件效果,我们用统一的方式(AudioPluginUtil.h和AudioPluginUtil.cpp)通过添加支持代码来解决效果定义和参数注册问题。注意NativePluginDemo例子中包含了一些示例插件以便开始学习并展示了在一个游戏场景下不同类型插件的用途。我们已将这些代码公开,你可以在自己的游戏中随便使用这些代码。

Developmentof a plugin starts with defining which parameters your plugin should have. Youdon’t need to have a detailed master plan of all the parameters that the pluginwill have laid out before you start, but it helps to roughly have an idea ofhow you want the user experience to be and what components you will need.

开发插件从定义插件需要的参数开始。虽然不需要你一开始就有一个面面俱到的参数表,但有关于用户体验和希望使用组件的大致认识是不无裨益的。

Theexample plugins that we provide have a bunch of utility functions that make iteasy Let’s take a look at the“Ring Modulator”example plugin. This simpleplugin multiplies the incoming signal by a sine wave, which gives a niceradio-noise / broken reception like effect, especially if multiple ringmodulation effects with different frequencies are chained.

提供的示例插件包括一系列功能函数以方便使用,在“Ring Modulator”例子插件中,将输入信号与sine波形复合,产生一种radio-noise / broken reception(收音机噪声/接收中断)似的效果,特别是将多个不同频率的ring modulation效果串联起来后。

Thebasic scheme for dealing with parameters in the example plugins is to definethem as enum-values that we use as indices into an array of floats for bothconvenience and brevity.

例子插件中处理参数的基本方式是将它定义为枚举值,为了方便和简洁我们就用浮点数组来索引这些枚举值。

enum Param

{

P_FREQ,

P_MIX,

P_NUM

};

intInternalRegisterEffectDefinition(UnityAudioEffectDefinition& definition)

{

intnumparams = P_NUM;

definition.paramdefs = new UnityAudioParameterDefinition [numparams];

RegisterParameter(definition, "Frequency", "Hz",

0.0f,kMaxSampleRate, 1000.0f,

1.0f,3.0f,

P_FREQ);

RegisterParameter(definition, "Mix amount", "%",

0.0f,1.0f, 0.5f,

100.0f, 1.0f,

P_MIX);

returnnumparams;

}

Thenumbers in the RegisterParameter calls are the minimum, maximum and defaultvalues followed by a scaling factor used for display only, i.e. in the case ofa percentage-value the actual value goes from 0 to 1 and is scaled by 100 whendisplayed. There is no custom GUI code for this, but as mentioned earlier,Unity will generate a default GUI from these basic parameter definitions. Notethat no checks are performed for undefined parameters, so the AudioPluginUtilsystem expects that all declared enum values (except P_NUM) are matched up witha corresponding parameter definition.

在RegisterParameter调用时使用的参数包括一个最小值、最大值和缺省值以及一个仅用于显示的缩放因子,这是一个在0到1之间的百分比,在显示时会放大100倍。这个例子中没有自定义的GUI,像之前提到的,Unity会为这些基本的参数定义提供一个缺省的界面。注意,未定义的参数不会进行检查,因此在AudioPluginUtil中的每个参数都应与声明的枚举值(除P_NUM外)一一对应。

Behindthe scenes the RegisterParameter function fills out an entry in theUnityAudioParameterDefinition array of the UnityAudioEffectDefinition structurethat is associated with that plugin (see“AudioEffectPluginInterface.h”). Therest that needs to be set up in UnityAudioEffectDefinition is the callbacks tothe functions that handle instantiating the plugin (CreateCallback),setting/getting parameters (SetFloatParameterCallback/UnityAudioEffect_GetFloatParameterCallback),doing the actual processing (UnityAudioEffect_ProcessCallback) and eventuallydestroying the plugin instance when done (UnityAudioEffect_ReleaseCallback).

在场景之后,RegisterParameter函数填充了UnityAudioParameterDefinition数组中的一个条目,该条目为UnityAudioEffectDefinition类型的结构体,该结构体与插件相关(参见“AudioEffectPluginInterface.h”)。在UnityAudioEffectDefinition中剩下要设置是回调函数:处理插件初始化的函数(CreateCallback),包括:设置/获取参数(SetFloatParameterCallback/UnityAudioEffect_GetFloatParameterCallback);进行音频处理的函数(UnityAudioEffect_ProcessCallback),和最终销毁插件实例的回调函数(UnityAudioEffect_ReleaseCallback)。

Tomake it easy to have multiple plugins in the same DLL, each plugin is residingin its own namespace, and a specific naming convention for the callbackfunctions is used such that the DEFINE_EFFECT and DECLARE_EFFECT macros canfill out the UnityAudioEffectDefinition structure. Underneath the hood all theeffects definitions are stored in an array to which a pointer is returned bythe only entry point of the library UnityGetAudioEffectDefinitions.

为在一个DLL中实现多个插件,每个插件都必须处于自身的命名空间中,此外,还使用了一个特定的回调函数名称转换机制,这样就可以用DEFINE_EFFECT和DECLARE_EFFECT宏填写UnityAudioEffectDefinition结构体了。为了封装效果,所有效果定义都存储在一个数组中,并在动态库UnityGetAudioEffectDefinitions的唯一访问点提供返回指向该数组的指针。

Thisis useful to know in case you want to develop bridge plugins that map fromother plugin formats such as VST or AudioUnits to or from the Unity audioplugin interface, in which case you need to develop a more dynamic way to setup the parameter descriptions at load time.

如果你想开发如:从VST或AudioUnits向Unity音频插件接口双向映射的bridge插件的话,你就需要建立一种更加动态化的方法去设置加载时的参数描述。

Instantiating the plugin

实例化插件

Thenext thing is the data for the instance of the plugin. In the example plugins,we put all this into the EffectData structure. The allocation of this musthappen in the corresponding CreateCallback which is called for each instance ofthe plugin in the mixer. In this simple example there’s only one sine-wave thatis multiplied to all channels, other more advanced plugins need allocateadditional data per input channel.

下一个要处理的是插件实例的数据。在例子插件中,我把它们全部放进EffectData结构体中,分配必须在混合器为每一个插件实例调用的相应的CreateCallback函数中进行。在这个简单的例子中,只将sin波形与所有的信道相叠加。而在更加复杂的插件中需要为每个输入信道单独分配附加的数据。

struct EffectData

{

struct Data

{

floatp[P_NUM]; // Parameters  //参数

floats;        // Sine output of oscillator  // sine波形输出

floatc;        // Cosine output of oscillator  // cosine波形输出

};

union

{

Datadata;

unsignedchar pad[(sizeof(Data) + 15) & ~15];

};

};

Waita minute! What’s all that union and padding stuff about? Well, unless youdevelop your plugin for the PlayStation 3 you can ignore this and just focus onthe members inside the“Data”structure, but in case you want to port yourplugin to this platform you need to be aware that on PS3 the signal processingis happening on the SPUs and in order to transfer data back and forth betweenthe main CPU and the SPU you need to make sure that the data is aligned to a 16byte boundary, and thus the“pad”array makes sure that the total size of theEffectData structure will be a multiple of 16 even though the actual Datastructure where our plugin data lives may not be divisible by 16. Thisrestriction makes the code a little funkier to look at, but in the end thebenefit is that there’s only one piece of shared code to maintain on allplatforms and that it’s easy to port your code to the PS3 if you follow the wayit’s done in the examples.

在此还是要稍微解释以下union和padding的作用的?当然,如果你不是在为PlayStation3平台开发插件,你可以忽略这两个参数,而集中注意力于Data结构体中的成员变量。但如果你希望插件应用于PS3平台的,你就需要知道,在PS3上信号处理是由SPU进行的,为了在CPU和SU之间来回传输数据,你得保证数据是16位边界对齐的。这样,padding数组就是为了保证EffectData结构体大小正好是16的整数倍,即使真实数据的大小不一定能被16整除。虽然这种实现方式可能使代码看起来有点难,但却可以获得在所有平台上一致的代码。所以,如果你按照例子编码,那么恭喜啦,你的代码就容易向PS3移植。

UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACKCreateCallback(

UnityAudioEffectState* state)

{

EffectData*effectdata = new EffectData;

memset(effectdata, 0, sizeof(EffectData));

effectdata->data.c = 1.0f;

state->effectdata= effectdata;

InitParametersFromDefinitions(

InternalRegisterEffectDefinition, effectdata->data.p);

returnUNITY_AUDIODSP_OK;

}

TheUnityAudioEffectState contains various data from the host such as the samplingrate, the total number of samples processed (for timing), or whether the pluginis bypassed, and is passed to all callback functions.

UnityAudioEffectState包含了从调用程序输入的数据,包括:采样率、处理样本数(为计时),是否忽略插件、并将这些参数传递给所有的回调函数。

Andobviously to free the plugin instance there is a corresponding function too:

很明显,也需要负责释放插件实例的函数

UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACKReleaseCallback(

UnityAudioEffectState* state)

{

EffectData::Data* data =&state->GetEffectData()->data;

delete data;

returnUNITY_AUDIODSP_OK;

}

Themain processing of audio happens in the ProcessCallback:

主要的音频处理在ProcessCallback中进行:

UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACKProcessCallback(

UnityAudioEffectState* state,

float*inbuffer, float* outbuffer,

unsigned intlength,

intinchannels, int outchannels)

{

EffectData::Data* data =&state->GetEffectData()->data;

float w =2.0f * sinf(kPI * data->p[P_FREQ] / state->samplerate);

for(unsignedint n = 0; n < length; n++)

{

for(inti = 0; i < outchannels; i++)

{

outbuffer[n * outchannels + i] =

inbuffer[n * outchannels + i] *

(1.0f - data->p[P_MIX] + data->p[P_MIX] * data->s);

}

data->s += data->c * w; // cheap way to calculate a sine-wave //计算sine波形的低代价方法

data->c -= data->s * w;

}

returnUNITY_AUDIODSP_OK;

}

We’vestripped the PS3-specific parts from this function in this listing. TheGetEffectData function at the top is just a helper function casting the effectdatafield of the state variable to the EffectData::Data in the structure wedeclared above.

在列出的函数中我们略过了为PS3处理的部分,最前面的GetEffectData函数用于将状态变量effectdata转换成我们上面声明的结构体EffectData::Data类型。

Othersimple plugins included are the NoiseBox plugin, which adds and multiplies theinput signal by white noise at variable frequencies, or the Lofinator plugin,which does simple downsampling and quantization of the signal. All of these maybe used in combination and with game-driven animated parameters to simulateanything from mobile phones to bad radio reception on walkies, brokenloudspeakers etc.

其他的简单插件包括NoiseBox插件,实现为输入信号添加或复合各种频率的随机白噪声的功能;Lofinator插件,对信号进行简单的降频采样和量化。这些插件可以组合使用或者基于游戏驱动的动画参数来模拟从移动电话响声到未校准通讯频率的步话机,坏了的扩音机,等等效果。

TheStereoWidener, which decomposes a stereo input signal into mono and sidecomponents with variable delay and then recombines these to increase theperceived stereo effect.

StereoWidener将输入的立体声信号分解到单声道和具有不同延迟的音源方向组件,接着重新组合这些信号以增强立体声效果感受。

A bunch of simple plugins withoutcustom GUIs to get started with.

大量可以用于起步学习的简单且没有自定义GUI界面的示例插件。

Which plugin to load on which platform?

为不同平台加载不同插件

Nativeaudio plugins use the same scheme as other native or managed plugins in thatthey must be associated with their respective platforms via the plugin importerinspector. You can read more about the subfolders in which to place plugins here. The platform association is necessary so that thesystem knows which plugins to include on a each build target in the standalonebuilds, and with the introduction of 64-bit support this even has to bespecified within a platform. OSX plugins are special in this regard since theUniversal Binary format allows them to contain both 32 and 64 bit variants inthe same bundle.

原生音频插件和其他原生及受管插件使用方式一样:它们必须通过引入插件的观察器建立与不同平台的关联。你可以阅读此处以了解这些组件需要被设置在哪些子文件夹中。平台相关性是必须要考虑的,这样系统才能针对不同的平台构建独立应用时选择加载对应的插件,对于64位平台更是如此。不过,OSX的插件就比较爽,通用的二进制格式使得它能在一个包中同时提供32位和64位版本。

Nativeplugins in Unity that are called from managed code get loaded via the[DllImport] attribute referencing the function to be imported from the nativeDLL. However, in the case of native audio plugins things are different. Thespecial problem that arises here is that the audio plugins need to be loadedbefore Unity starts creating any mixer assets that may need effects from theplugins. In the editor this is no problem, because we can just reload andrebuild the mixers that depend on plugins, but in standalone builds the pluginsmust be loaded before we create the mixer assets. To solve this, the currentconvention is to prefix the DLL of the plugin“audioplugin”(case insensitive)so that the system can detect this and add it to a list of plugins that willautomatically be loaded at start. Remember that it’s only the definitionsinside the plugin that define the names of the effects that show up insideUnity’s mixer, so the DLL can be called anything, but it needs to start withthe string“audioplugin”to be detected as such.

Unity中的原生插件在受管代码中调用,从本地的DLL文件通过[DllImport]属性引用导入函数。然而原生音频插件却不相同,问题的原因在于,音频插件必须在Unity开始创建需要使用其效果的混音器资源创建之前先行加载,在编辑器环境中,这没有什么问题,因为我们可以通过重新加载并重构建依赖插件的混音器解决此问题,但在独立发布的游戏中,必须在混音器创建前完成插件加载。为了解决这个问题,惯常的做法是在插件DLL中加入audioplugin前缀(大小写敏感),这样系统就可以检测到音频插件,并将其添加到启动时自动加载的插件列表中。注意,只有在插件内部的定义才会决定显示在Unity混音器中的名称和效果,因此DLL文件可以随意命名,但是它需要以字符串audioplugin开始才能被检测和加载。

Forplatforms such as IOS the plugin code needs to be statically linked into theUnity binary produced by the generated XCode project and there - just likeplugin rendering devices - the plugin registration has to be added explicitlyto the startup code of the app.

对于IOS这类的平台,插件代码需要静态链接到Unity二进制文件中(通过生成的XCode工程——就像渲染设备插件一样——插件注册必须以显式的方式添加到app的启动代码中)。

OnOSX one bundle can contain both the 32- and 64 bit version of the plugin. Youcan also split them to save size.

OSX中,一个包可以包含32位和64位版本的插件,你也可以分开以减少体积。

Plugins with custom GUIs

带自定义GUI的插件

Nowlet’s look at something a little more advanced: Effects for equalization andmultiband compression. Such plugins have a much higher number of parametersthan the simple plugins presented in the previous section and also there issome physical coupling between parameters that require a better way tovisualize the parameters than just a bunch of simple sliders. Consider anequalizer for instance: Each band has 3 different filters that collectivelycontribute to the final equalization curve and each of these filters has the 3parameters frequency, Q-factor and gain which are physically linked and definethe shape of each filter. So it helps the user a lot, if an equalizer pluginhas a nice big display showing the resulting curve, the individual filter contributionsand can be operated in such a way that multiple parameters can be setsimultaneously by simple dragging operations on the control instead of changingsliders one at a time.

现在学习些更高级的内容:均衡和多波段压缩效果。这些插件比前面说的简单插件需要的参数多得多,同时,参数间存在的物理相关性使得仅用一系列滑杆条不足以展示它们。以均衡器为例:每个波段都有三个不同的滤波器共同控制均衡曲线而且这三个滤波器每个都有:频率、Q-因子和增益三个参数,这三个参数物理相关确定了滤波器的形状。因此,为均衡器插件提供一个大的显示结果曲线的界面,其中每个滤波器的作用都可以在此操作,各个参数也可以通过简单的拖拽操作在此设置而不是每次修改滑杆条进行设置是很有必要的。

CustomGUI of the Equalizer plugin. Drag the three bands to change the gains and frequenciesof the filter curve. Hold shift down while dragging to change the shape of eachband.

均衡器插件的自定义的GUI,支持以拖放三个波段的方式修改滤波曲线的增益和频率,在拖放时按下shift键来修改每个波段的形状。

Soonce again, the definition, initialization, deinitialization and parameterhandling follows the exact same enum-based method that the simple plugins use,and even the ProcessCallback code is rather short. Well, time to stop lookingat the native code and open the AudioPluginDemoGUI.sln project in MonoDevelop.Here you will find the associated C# classes for the GUI code. The way it worksis simple: Once Unity has loaded the native plugin DLLs and registered thecontained audio plugins, it will start looking for corresponding GUIs thatmatch the names of the registered plugins. This happens through the Nameproperty of the EqualizerCustomGUI class which, like all custom plugin GUIs,must inherit from IAudioEffectPluginGUI. There’s only one important functioninside this class which is the bool OnGUI(IAudioEffectPlugin plugin) function.Via the IAudioEffectPlugin plugin argument this function gets a handle to thenative plugin that it can use to read and write the parameters that the nativeplugin has defined. So to read a parameter it calls:

plugin.GetFloatParameter("MasterGain", outmasterGain);

whichreturns true if the parameter was found, and to set it, it calls:

plugin.SetFloatParameter("MasterGain",masterGain);

whichalso returns true if the parameter exists. And that’s basically the mostimportant binding between GUI and native code. You can also use the function

plugin.GetFloatParameterInfo("NAME", outminVal, out maxVal, out defVal);

带有自定义的GUI的音频插件的定义、初始化、反初始化和参数控制与简单插件中使用基于枚举值的方法是完全一致的,ProcessCallback的代码甚至可以更短,现在可以关闭原生代码学习GUI开发了,在MonoDevelop中打开AudioPluginDemoGUI.sln工程,在此你可以看到与GUI代码相关的C#类,其工作方式简单:一旦Unity完成原生插件的DLL的加载并注册其中的音频插件,它就会通过EqualizerCustomGUI类的名称属性来寻找和注册的插件名称对应的GUI,和所有的带自定义GUI音频插件一样,必须继承自IAudioEffectPluginGUI接口。在此类中只有一个重要的函数:bool OnGUI(IAudioEffectPlugin plugin),通过函数参数IAudioEffectPluginplugin为函数提供了一个原生插件的句柄,可以用来读写原生插件定义中的相关参数,其中,方法plugin.GetFloatParameter("MasterGain",out masterGain);用于读取一个参数设置,若该函数返回真,则参数被发现;为了设置参数需要调用函数:plugin.SetFloatParameter("MasterGain", masterGain);

和读取函数一样,该函数在参数存在的情况下返回真值。基本上,这些是GUI和原生代码之间最重要的关联函数。也可以用函数plugin.GetFloatParameterInfo("NAME", out minVal, outmaxVal, out defVal);

来查询名称为“NAME”的参数的最小值、最大值和缺省值以避免在原生库和UI代码中出现重复定义。注意,如果OnGUI函数返回真,inspector会在自定义GUI下显示缺省UI滑杆条控制。这对开始进行GUI开发是大有裨益的,因为这样你可以在开发自定义GUI时访问到所有参数,进而检查在参数修改时插件的行为是否正确。

toquery parameter“NAME”for it’s minimum, maximum and default values to avoidduplicate definitions of these in the native and UI code. Note that if yourOnGUI function return true, the Inspector will show the default UI slidersbelow the custom GUI. This is again useful to bootstrap your GUI development asyou have all the parameters available while developing your custom GUI and havean easy way to check that the right actions performed on it result in theexpected parameter changes.

在此不详谈均衡器和多波段插件的DSP库处理过程,感兴趣的话,这些过滤器资料来自于Robert Bristow Johnson超牛的音频EQ Cookbook,其中曲线绘制使用了Unity提供的一些内部API函数来绘制频率反馈的抗锯齿曲线。

Wewon’t discuss the details about the DSP processing that is going on in theEqualizer and Multiband plugins here, for those interested, the filters aretaken from Robert Bristow Johnson’s excellent Audio EQ Cookbook and to plot thecurves Unity provides some internal API functions to draw antialiased curvesfor the frequency response.

还有一点要提一下:均衡器和多波段插件都包含将输入和输出谱叠加显示的代码实现插件的可视化,这提供了有趣的一点,GUI更新频率(帧率)相比音频处理代码更新率要低,此外,GUI代码不能访问音频流,那么如何读取数据?为此,原生代码中提供了一个特定的函数实现该功能:

Onemore thing to mention though is that both the Equalizer and Multiband pluginsdo also provide code to overlay the input and output spectra to visualize theeffect of the plugins, which brings up an interesting point: The GUI code runsat much lower update rate (the frame rate) than the audio processing anddoesn’t have access to the audio streams, so how do we read this data? Forthis, there is a special function for this in the native code:

UNITY_AUDIODSP_RESULT UNITY_AUDIODSP_CALLBACKGetFloatParameterCallback(

UnityAudioEffectState* state,

int index,

float*value,

char*valuestr)

{

EffectData::Data* data =&state->GetEffectData()->data;

if(index>= P_NUM)

returnUNITY_AUDIODSP_ERR_UNSUPPORTED;

if(value !=NULL)

*value =data->p[index];

if(valuestr!= NULL)

valuestr[0] = 0;

returnUNITY_AUDIODSP_OK;

}

Itsimply enables reading an array of floating-point data from the native plugin.Whatever that data is, the plugin system doesn’t care about, as long as therequest doesn’t massively slow down the UI or the native code. For theEqualizer and Multiband code there is a utility class called FFTAnalyzer whichmakes it easy to feed in input and output data from the plugin and get aspectrum back. This spectrum data is then resampled by GetFloatBufferCallbackand handed to the C# UI code. The reason that the data needs to be resampled isthat the FFTAnalyzer runs the analysis at a fixed frequency resolution whileGetFloatBufferCallback just returns the number of samples requested, which isdetermined by the width of the view that is displaying the data. For a verysimple plugin that has a minimal amount of DSP code you might also take a lookat the CorrelationMeter plugin, which simply plots the amplitude of the leftchannel against the amplitude of the right channel in order to show“howstereo”the signal is.

该函数允许从原生插件中读取浮点数组,而不管该数据的意义。另一方面,只要该处理不显著降低UI和原生代码的效率插件系统也不会在意。在均衡器和多波段代码中有一个FFTAnalyzer功能类,实现了从插件获取输入并计算频谱并输出数据的功能。频谱数据通过GetFloatBufferCallback函数重采样再交由C#的UI代码处理。重采样的原因在于FFTAnalyzer按照固定的频率分辨率执行而GetFloatBufferCallback仅需采样返回显示视图大小需要的数据。一个非常简单且包含最小的DSP代码的插件是CorrelationMeter,用于绘制左通道幅值相对于右通道幅值以展示立体声的形态。

Left:Custom GUI of the CorrelationMeter plugin.

左:CorrelationMeter插件的自定义GUI

Right:Equalizer GUI with overlaid spectrum analysis (green curve is source, red isprocessed).

右:带频谱分析的均衡器GUI(绿色曲线是源音频,红色为处理后)

Atthis point we would also like to point out that both the Equalizer and Multibandeffects are kept intentionally simple and unoptimized, but we think they serveas good examples of more complex UIs that are supported by the plugin system.There’s obviously a lot of work still in doing the relevant platform-specificoptimizations, tons of parameter tweaking to make it fell really right andrespond in the most musical way etc. etc…We might also implement some of theseeffects as built-in plugins in Unity at some point simply for the convenienceof increasing Unity’s standard repertoire of plugins, but we sincerely hopethat the reader will also take up the challenge to make some really awesomeplugins–and who knows, they might at some point end up as built-in plugins.;-)

在此需要指出,我们故意让均衡器和多波段效果是保持简单,且未进行优化。但它们都是具有更复杂UI界面的音频插件的好例子,针对专门平台的优化还有还有大量的工作,需要成吨的参数调节才能获得正确的感受和最音乐化效果的反馈。我们也可以实现一些这类效果的插件作为Unity的内建插件以增加Unity标准插件库,但是我们真心希望读者们接受挑战来制作一些真正奥萨母的插件——说不好它们会成为内置插件呢?;-)

Convolutionreverb example plugin. The impulse response is decaying random noise, definedby the parameters. This is only for demonstration purposes, as a productionplugin should allow the user to load arbitrary recorded impulses, theunderlying convolution algorithm remains the same nevertheless.

Convolutionreverb示例插件。冲击响应是由参数定义的不断衰减的随机噪声。在此只是为了演示,如果是产品,应当为用户提供加载任意的冲击记录支持,然而底层的环绕算法仍然是一样的。

Exampleof a loudness monitoring tool measuring levels at 3 different time scales. Alsojust for demonstration purposes, but a good place to start building a monitortool that conforms to modern loudness standardizations. The curve renderingcode is built into Unity.

音量监控工具在三个不同的时间尺度测量音量水平。一样只是为了演示目的,但这是个建立符合现代音量标准的监控工具好开始,其中,曲线渲染使用了Unity的内置代码。

Synchronizing to the DSP clock

与DSP时钟同步

Timefor some fun exercises. Why not use the plugin system to generate sound insteadof just processing it? Let’s try to do some simple bassline and drumsynthesizers that should be familiar to people who listen to acid trance–somesimple clones of some of the main synths that defined this genre. Take a lookat Plugin_TeeBee.cpp and Plugin_TeeDee.cpp. These simple synths just generatepatterns with random notes and have some parameters for tweaking the filters,envelopes and so forth in the synthesis engine. Again, we won’t discuss thosedetails here, but just point out that the state->dsptick parameter is readin the ProcessCallback in order to determine the position in the“song”. Thiscounter is a global sample position, so we just divide it by the length of eachnote specified in samples and fire a note event to the synthesis enginewhenever this division has a zero remainder. This way, all plugin effects stayin sync to the same sample-based clock, and ifyou would for instance play a prerecorded piece of music with a known tempothrough such an effect, you could use the timing info to applytempo-synchronized filter effects or delays on the music.

是时候做些有趣的工作了,与其只是处理音频,也可以使用插件系统产生声音哦。咱们从一个简单的单音节鼓点合成器开始,听acid trance的人应该很熟悉这种风格,通过简单的复制定义了这种风格的基本合成器来实现这种效果。代码在Plugin_TeeBee.cpp和Plugin_TeeDee.cpp中。该合成器使用随机音符生成鼓点并具有一些用于调整滤波器、封包等的参数。在此我们还是不涉及具体实现,但是要指出的是state->dsptick参数会被传入ProcessCallback函数中以确定“歌曲”的位置,这个计数器代表了一个全局的采样位置,我们用计数器除以采样中指定的每个音符的长度,每当整除时,就发出一个音节,以此实现插件效果与采样时钟的同步。如果你打算用此插件播放一个节拍已知的预录制的音乐,你可以用计时信息设置节拍-同步滤波器效果或者音乐播放的延迟。

Simple bassline and drum synthesizers to demonstratetempo-synchronized effects.

简单单音节鼓点合成器演示节奏同步效果

Spatialization

空间化

Thenative audio plugin SDK is the foundation of the Spatialization SDK whichallows developing custom spatialization effects that are instantiazed per audiosource. More information about this can be found here.

原生音频插件SDK是空间化SDK的基础,空间化SDK通过实例化每个声音源组件以实现自定义空间效果。更多内容可以在此查看。

Outlook

前瞻

Thisis just the start of an effort to open up parts of the sound system to highperformance native code. We have plans to integrate this in other parts ofUnity as well to make the effects usable outside of the mixer as well asextending the SDK to support other parameter types than floats with support forbetter default GUIs as well as storage of binary data.

这些仅是将部分声音系统放到高性能原生代码实现的开始,我们已有计划将这个部分与Unity的其他部分进行集成,以支持在混音器外使用插件效果,同时扩展SDK以支持float外的其他参数类型,提供更好的缺省GUI界面和二进制数据存储支持。

Havea lot of fun creating your own plugins. Hope to see them on the asset store.;-)

创建你自己的插件很有趣,希望能在Unity的Asset商店见到它们。;-)

“Disclaimer”

为什么不提供更多插件

Whilethere are many similarities in the design, Unity’s native audio SDK is notbuilt on top of other plugin SDKs like Steinberg VST or Apple AudioUnits. Itshould be mentioned that it would be easy for the interested reader toimplement basic wrappers for these using this SDK that allow using such pluginsto be used in Unity. It is not something the dev-team of Unity is planning todo. Proper hosting of any plugin quickly gets very complex, and dealing withall the intricacies of expected invocation orders and handling custom GUIwindows that are based on native code quickly grows by leaps and bounds whichmakes it less useful as example code.

虽然设计上存在相似性,但是Unity的原生音频SDK不像Steinberg VST或AppleAudioUnits一样在其他插件SDK的基础上实现。顺便一提,感兴趣的读者使用此SDK轻松实现对它们进行基本封装并在Unity中使用。这不是开发团队计划要做的事,合理容纳任意插件将使得Unity变得十分复杂,而管理错综复杂的期望调用顺序和基于原生代码的自定义GUI窗口的处理需求会急速增长,而这些都使得示例代码的更有意义。

Whilewe do understand that it could potentially be quite useful to load your VST orAU plugin or even effects for just mocking up / testing sound design, bear inmind that using VST/AU also limits you to a few specific platforms. Thepotential of writing audio plugins based on the Unity SDK is that it extends toall platforms that support software-mixing and dynamically loaded native code.That said, there are valid use cases for mocking up early sound design withyour favourite tools before deciding to devote time to develop custom plugins(or simply to be able to use metering plugins in the editor that don’t alterthe sound in any way), so if anyone wants to make a nice solution for that,please do.

我们当然知道提供VST或AU插件,甚至只是用于模仿和测试声音设计的效果会为用户带来益处,但请记住,使用VST/AU也会将你限制在特定的平台上,编写开发基于Unity SDK的音频插件的优势就在于它可以在所有平台实现软件混音和原生代码的动态加载支持。也就是说它可以支持以下的用例:用自己熟悉的工具进行早期音效设计,进而决定是否花时间进行自定义插件开发(或者简单使用编辑器中的metering插件,而无需对声音进行任何修改)。因此,如果你想得到优秀的解决方案,就请使用它吧

a

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容