/************************************************************************************
|
|
Filename : OVRLipSync.cs
|
|
Content : Interface to Oculus Lip Sync engine
|
|
Created : August 4th, 2015
|
|
Copyright : Copyright Facebook Technologies, LLC and its affiliates.
|
|
All rights reserved.
|
|
|
|
Licensed under the Oculus Audio SDK License Version 3.3 (the "License");
|
|
you may not use the Oculus Audio SDK except in compliance with the License,
|
|
which is provided at the time of installation or download, or which
|
|
otherwise accompanies this software in either electronic or hard copy form.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
https://developer.oculus.com/licenses/audio-3.3/
|
|
|
|
Unless required by applicable law or agreed to in writing, the Oculus Audio SDK
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
************************************************************************************/
|
|
using UnityEngine;
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// ***** OVRLipSync
|
|
//
|
|
/// <summary>
|
|
/// OVRLipSync interfaces into the Oculus lip sync engine. This component should be added
|
|
/// into the scene once.
|
|
///
|
|
/// </summary>
|
|
public class OVRLipSync : MonoBehaviour
|
|
{
|
|
// Error codes that may return from Lip Sync engine
|
|
public enum Result
|
|
{
|
|
Success = 0,
|
|
Unknown = -2200, //< An unknown error has occurred
|
|
CannotCreateContext = -2201, //< Unable to create a context
|
|
InvalidParam = -2202, //< An invalid parameter, e.g. NULL pointer or out of range
|
|
BadSampleRate = -2203, //< An unsupported sample rate was declared
|
|
MissingDLL = -2204, //< The DLL or shared library could not be found
|
|
BadVersion = -2205, //< Mismatched versions between header and libs
|
|
UndefinedFunction = -2206 //< An undefined function
|
|
};
|
|
|
|
// Audio buffer data type
|
|
public enum AudioDataType
|
|
{
|
|
// Signed 16-bit integer mono audio stream
|
|
S16_Mono,
|
|
// Signed 16-bit integer stereo audio stream
|
|
S16_Stereo,
|
|
// Signed 32-bit float mono audio stream
|
|
F32_Mono,
|
|
// Signed 32-bit float stereo audio stream
|
|
F32_Stereo
|
|
};
|
|
|
|
// Various visemes
|
|
public enum Viseme
|
|
{
|
|
sil,
|
|
PP,
|
|
FF,
|
|
TH,
|
|
DD,
|
|
kk,
|
|
CH,
|
|
SS,
|
|
nn,
|
|
RR,
|
|
aa,
|
|
E,
|
|
ih,
|
|
oh,
|
|
ou
|
|
};
|
|
|
|
public static readonly int VisemeCount = Enum.GetNames(typeof(Viseme)).Length;
|
|
|
|
// Enum for sending lip-sync engine specific signals
|
|
public enum Signals
|
|
{
|
|
VisemeOn,
|
|
VisemeOff,
|
|
VisemeAmount,
|
|
VisemeSmoothing,
|
|
LaughterAmount
|
|
};
|
|
|
|
public static readonly int SignalCount = Enum.GetNames(typeof(Signals)).Length;
|
|
|
|
// Enum for provider context to create
|
|
public enum ContextProviders
|
|
{
|
|
Original,
|
|
Enhanced,
|
|
Enhanced_with_Laughter,
|
|
};
|
|
|
|
/// NOTE: Opaque typedef for lip-sync context is an unsigned int (uint)
|
|
|
|
/// Current phoneme frame results
|
|
[System.Serializable]
|
|
public class Frame
|
|
{
|
|
public void CopyInput(Frame input)
|
|
{
|
|
frameNumber = input.frameNumber;
|
|
frameDelay = input.frameDelay;
|
|
input.Visemes.CopyTo(Visemes, 0);
|
|
laughterScore = input.laughterScore;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
frameNumber = 0;
|
|
frameDelay = 0;
|
|
Array.Clear(Visemes, 0, VisemeCount);
|
|
laughterScore = 0;
|
|
}
|
|
|
|
public int frameNumber; // count from start of recognition
|
|
public int frameDelay; // in ms
|
|
public float[] Visemes = new float[VisemeCount]; // Array of floats for viseme frame. Size of Viseme Count, above
|
|
public float laughterScore; // probability of laughter presence.
|
|
};
|
|
|
|
// * * * * * * * * * * * * *
|
|
// Import functions
|
|
#if !UNITY_IOS || UNITY_EDITOR
|
|
public const string strOVRLS = "OVRLipSync";
|
|
#else
|
|
public const string strOVRLS = "__Internal";
|
|
#endif
|
|
[DllImport(strOVRLS)]
|
|
private static extern int ovrLipSyncDll_Initialize(int samplerate, int buffersize);
|
|
[DllImport(strOVRLS)]
|
|
private static extern void ovrLipSyncDll_Shutdown();
|
|
[DllImport(strOVRLS)]
|
|
private static extern IntPtr ovrLipSyncDll_GetVersion(ref int Major,
|
|
ref int Minor,
|
|
ref int Patch);
|
|
[DllImport(strOVRLS)]
|
|
private static extern int ovrLipSyncDll_CreateContextEx(ref uint context,
|
|
ContextProviders provider,
|
|
int sampleRate,
|
|
bool enableAcceleration);
|
|
|
|
[DllImport(strOVRLS)]
|
|
private static extern int ovrLipSyncDll_CreateContextWithModelFile(ref uint context,
|
|
ContextProviders provider,
|
|
string modelPath,
|
|
int sampleRate,
|
|
bool enableAcceleration);
|
|
|
|
[DllImport(strOVRLS)]
|
|
private static extern int ovrLipSyncDll_DestroyContext(uint context);
|
|
|
|
|
|
[DllImport(strOVRLS)]
|
|
private static extern int ovrLipSyncDll_ResetContext(uint context);
|
|
[DllImport(strOVRLS)]
|
|
private static extern int ovrLipSyncDll_SendSignal(uint context,
|
|
Signals signal,
|
|
int arg1, int arg2);
|
|
[DllImport(strOVRLS)]
|
|
private static extern int ovrLipSyncDll_ProcessFrameEx(
|
|
uint context,
|
|
IntPtr audioBuffer,
|
|
uint bufferSize,
|
|
AudioDataType dataType,
|
|
ref int frameNumber,
|
|
ref int frameDelay,
|
|
float[] visemes,
|
|
int visemeCount,
|
|
ref float laughterScore,
|
|
float[] laughterCategories,
|
|
int laughterCategoriesLength);
|
|
|
|
// * * * * * * * * * * * * *
|
|
// Public members
|
|
|
|
// * * * * * * * * * * * * *
|
|
// Static members
|
|
private static Result sInitialized = Result.Unknown;
|
|
|
|
// interface through this static member.
|
|
public static OVRLipSync sInstance = null;
|
|
|
|
|
|
// * * * * * * * * * * * * *
|
|
// MonoBehaviour overrides
|
|
|
|
/// <summary>
|
|
/// Awake this instance.
|
|
/// </summary>
|
|
void Awake()
|
|
{
|
|
// We can only have one instance of OVRLipSync in a scene (use this for local property query)
|
|
if (sInstance == null)
|
|
{
|
|
sInstance = this;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning(System.String.Format("OVRLipSync Awake: Only one instance of OVRPLipSync can exist in the scene."));
|
|
return;
|
|
}
|
|
|
|
if (IsInitialized() != Result.Success)
|
|
{
|
|
sInitialized = Initialize();
|
|
|
|
if (sInitialized != Result.Success)
|
|
{
|
|
Debug.LogWarning(System.String.Format
|
|
("OvrLipSync Awake: Failed to init Speech Rec library"));
|
|
}
|
|
}
|
|
|
|
// Important: Use the touchpad mechanism for input, call Create on the OVRTouchpad helper class
|
|
OVRTouchpad.Create();
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the destroy event.
|
|
/// </summary>
|
|
void OnDestroy()
|
|
{
|
|
if (sInstance != this)
|
|
{
|
|
Debug.LogWarning(
|
|
"OVRLipSync OnDestroy: This is not the correct OVRLipSync instance.");
|
|
return;
|
|
}
|
|
|
|
// Do not shut down at this time
|
|
// ovrLipSyncDll_Shutdown();
|
|
// sInitialized = (int)Result.Unknown;
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * *
|
|
// Public Functions
|
|
|
|
public static Result Initialize()
|
|
{
|
|
int sampleRate;
|
|
int bufferSize;
|
|
int numbuf;
|
|
|
|
// Get the current sample rate
|
|
sampleRate = AudioSettings.outputSampleRate;
|
|
// Get the current buffer size and number of buffers
|
|
AudioSettings.GetDSPBufferSize(out bufferSize, out numbuf);
|
|
|
|
String str = System.String.Format
|
|
("OvrLipSync Awake: Queried SampleRate: {0:F0} BufferSize: {1:F0}", sampleRate, bufferSize);
|
|
Debug.LogWarning(str);
|
|
|
|
sInitialized = (Result)ovrLipSyncDll_Initialize(sampleRate, bufferSize);
|
|
return sInitialized;
|
|
}
|
|
|
|
public static Result Initialize(int sampleRate, int bufferSize)
|
|
{
|
|
String str = System.String.Format
|
|
("OvrLipSync Awake: Queried SampleRate: {0:F0} BufferSize: {1:F0}", sampleRate, bufferSize);
|
|
Debug.LogWarning(str);
|
|
|
|
sInitialized = (Result)ovrLipSyncDll_Initialize(sampleRate, bufferSize);
|
|
return sInitialized;
|
|
}
|
|
|
|
public static void Shutdown()
|
|
{
|
|
ovrLipSyncDll_Shutdown();
|
|
sInitialized = Result.Unknown;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if is initialized.
|
|
/// </summary>
|
|
/// <returns><c>true</c> if is initialized; otherwise, <c>false</c>.</returns>
|
|
public static Result IsInitialized()
|
|
{
|
|
return sInitialized;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a lip-sync context.
|
|
/// </summary>
|
|
/// <returns>error code</returns>
|
|
/// <param name="context">Context.</param>
|
|
/// <param name="provider">Provider.</param>
|
|
/// <param name="enableAcceleration">Enable DSP Acceleration.</param>
|
|
public static Result CreateContext(
|
|
ref uint context,
|
|
ContextProviders provider,
|
|
int sampleRate = 0,
|
|
bool enableAcceleration = false)
|
|
{
|
|
if (IsInitialized() != Result.Success && Initialize() != Result.Success)
|
|
return Result.CannotCreateContext;
|
|
|
|
return (Result)ovrLipSyncDll_CreateContextEx(ref context, provider, sampleRate, enableAcceleration);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a lip-sync context with specified model file.
|
|
/// </summary>
|
|
/// <returns>error code</returns>
|
|
/// <param name="context">Context.</param>
|
|
/// <param name="provider">Provider.</param>
|
|
/// <param name="modelPath">Model Dir.</param>
|
|
/// <param name="sampleRate">Sampling Rate.</param>
|
|
/// <param name="enableAcceleration">Enable DSP Acceleration.</param>
|
|
public static Result CreateContextWithModelFile(
|
|
ref uint context,
|
|
ContextProviders provider,
|
|
string modelPath,
|
|
int sampleRate = 0,
|
|
bool enableAcceleration = false)
|
|
{
|
|
if (IsInitialized() != Result.Success && Initialize() != Result.Success)
|
|
return Result.CannotCreateContext;
|
|
|
|
return (Result)ovrLipSyncDll_CreateContextWithModelFile(
|
|
ref context,
|
|
provider,
|
|
modelPath,
|
|
sampleRate,
|
|
enableAcceleration);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Destroy a lip-sync context.
|
|
/// </summary>
|
|
/// <returns>The context.</returns>
|
|
/// <param name="context">Context.</param>
|
|
public static Result DestroyContext(uint context)
|
|
{
|
|
if (IsInitialized() != Result.Success)
|
|
return Result.Unknown;
|
|
|
|
return (Result)ovrLipSyncDll_DestroyContext(context);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the context.
|
|
/// </summary>
|
|
/// <returns>error code</returns>
|
|
/// <param name="context">Context.</param>
|
|
public static Result ResetContext(uint context)
|
|
{
|
|
if (IsInitialized() != Result.Success)
|
|
return Result.Unknown;
|
|
|
|
return (Result)ovrLipSyncDll_ResetContext(context);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends a signal to the lip-sync engine.
|
|
/// </summary>
|
|
/// <returns>error code</returns>
|
|
/// <param name="context">Context.</param>
|
|
/// <param name="signal">Signal.</param>
|
|
/// <param name="arg1">Arg1.</param>
|
|
/// <param name="arg2">Arg2.</param>
|
|
public static Result SendSignal(uint context, Signals signal, int arg1, int arg2)
|
|
{
|
|
if (IsInitialized() != Result.Success)
|
|
return Result.Unknown;
|
|
|
|
return (Result)ovrLipSyncDll_SendSignal(context, signal, arg1, arg2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process float[] audio buffer by lip-sync engine.
|
|
/// </summary>
|
|
/// <returns>error code</returns>
|
|
/// <param name="context">Context.</param>
|
|
/// <param name="audioBuffer"> PCM audio buffer.</param>
|
|
/// <param name="frame">Lip-sync Frame.</param>
|
|
/// <param name="stereo">Whether buffer is part of stereo or mono stream.</param>
|
|
public static Result ProcessFrame(
|
|
uint context, float[] audioBuffer, Frame frame, bool stereo = true)
|
|
{
|
|
if (IsInitialized() != Result.Success)
|
|
return Result.Unknown;
|
|
|
|
var dataType = stereo ? AudioDataType.F32_Stereo : AudioDataType.F32_Mono;
|
|
var numSamples = (uint)(stereo ? audioBuffer.Length / 2 : audioBuffer.Length);
|
|
var handle = GCHandle.Alloc(audioBuffer, GCHandleType.Pinned);
|
|
var rc = ovrLipSyncDll_ProcessFrameEx(context,
|
|
handle.AddrOfPinnedObject(), numSamples, dataType,
|
|
ref frame.frameNumber, ref frame.frameDelay,
|
|
frame.Visemes, frame.Visemes.Length,
|
|
ref frame.laughterScore,
|
|
null, 0
|
|
);
|
|
handle.Free();
|
|
return (Result)rc;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process short[] audio buffer by lip-sync engine.
|
|
/// </summary>
|
|
/// <returns>error code</returns>
|
|
/// <param name="context">Context.</param>
|
|
/// <param name="audioBuffer"> PCM audio buffer.</param>
|
|
/// <param name="frame">Lip-sync Frame.</param>
|
|
/// <param name="stereo">Whether buffer is part of stereo or mono stream.</param>
|
|
public static Result ProcessFrame(
|
|
uint context, short[] audioBuffer, Frame frame, bool stereo = true)
|
|
{
|
|
if (IsInitialized() != Result.Success)
|
|
return Result.Unknown;
|
|
|
|
var dataType = stereo ? AudioDataType.S16_Stereo : AudioDataType.S16_Mono;
|
|
var numSamples = (uint)(stereo ? audioBuffer.Length / 2 : audioBuffer.Length);
|
|
var handle = GCHandle.Alloc(audioBuffer, GCHandleType.Pinned);
|
|
var rc = ovrLipSyncDll_ProcessFrameEx(context,
|
|
handle.AddrOfPinnedObject(), numSamples, dataType,
|
|
ref frame.frameNumber, ref frame.frameDelay,
|
|
frame.Visemes, frame.Visemes.Length,
|
|
ref frame.laughterScore,
|
|
null, 0
|
|
);
|
|
handle.Free();
|
|
return (Result)rc;
|
|
}
|
|
|
|
}
|