API Example -- SALSA 2D Runtime Setup (SpriteController)

The following 2D runtime example code is provided to demonstrate how a 2D Sprite-based controller configuration can be implemented for a SALSA configuration. The details are nearly identical for all 2D controller types. It will add two sprite-based visemes to the SALSA configuration. NOTE: The sprites are basically blank and you would need to implement code to provide proper links/loading of your sprite assets.

This example code works but is somewhat theoretical and includes configuration for demonstration purposes only. Please check the comments for details and understand additional code will be required to implement into your project requirements. Please also change the namespace to your standard/required namespace. See the SALSA API documentation for API details.

using UnityEngine;
using CrazyMinnow.SALSA;

namespace DemoCode // change this to your custom namespace...
    /// <summary>
    /// The boxHead runtime script is included as an example of runtime implementation for SALSA and/or EmoteR.
    /// This script creates additional (multiple) components on a viseme and emote which are for demonstration
    /// purposes only and will not produce good results if left in place. Please remove these additional components
    /// from the say medium viseme and the second (look down) for best results.
    /// </summary>
    public class Salsa2DSpriteRuntime : MonoBehaviour
        public static void Salsa2DSpriteVisemeSetup(GameObject selectedObject)
            var controller = selectedObject.GetComponent<SpriteRenderer>();
            // add at least 2 sprites to the following list
            var sprites = new System.Collections.Generic.List<Sprite>();
            sprites.Add(new Sprite()); // programmatically add your sprite...
            sprites.Add(new Sprite()); // programmatically add your sprite...

            // Quick test to see if this object has the necessary stuff for SALSA
            if (controller == null)
                Debug.Log("This object does not have the required Renderer components.");

            // Configure AudioSource for demonstration.
            var audSrc = selectedObject.GetComponent<AudioSource>();
            if (audSrc == null)
                audSrc = selectedObject.AddComponent<AudioSource>();
            // Optional: set your audio source for your preferences...
            audSrc.playOnAwake = false;
            audSrc.loop = false;
            // Remember to add an AudioClip to your AudioSource...
            // audSrc.clip = someAudioClip;
            // Remember to play your AudioSource in your code (after configuration)
            // to start automatic lip-synchronization.
            // Something like: CrazyMinnow.SALSA.Salsa.audioSrc.Play();

            // Setup a new queue processor if we don't find one. Recommend using one
            // queue processor per character (especially advanced characters with many
            // components).
            var qp = selectedObject.GetComponent<QueueProcessor>();
            if (qp == null)
                qp = selectedObject.AddComponent<QueueProcessor>();

            // Configure SALSA
            var salsa = selectedObject.GetComponent<Salsa>();
            if (salsa == null)
                salsa = selectedObject.AddComponent<Salsa>();

            salsa.audioSrc = audSrc;
            salsa.queueProcessor = qp;

            // adjust salsa settings
            //  - data analysis settings
            salsa.autoAdjustAnalysis = true;
            salsa.autoAdjustMicrophone = false;
            salsa.audioUpdateDelay = 0.0875f;
            //  - advanced dynamics
            salsa.loCutoff = 0.015f;
            salsa.hiCutoff = 0.75f;
            salsa.useAdvDyn = true;
            salsa.advDynPrimaryBias = 0.40f;
            salsa.useAdvDynJitter = false;
            salsa.advDynJitterAmount = 0.10f;
            salsa.advDynJitterProb = 0.20f;
            salsa.advDynSecondaryMix = 0.0f;
            salsa.advDynRollback = 0.2f;

            // setup visemes
            salsa.visemes.Clear(); // start fresh

            // setup viseme 1 -- saySmall
            salsa.visemes.Add(new LipsyncExpression("saySmall", new InspectorControllerHelperData(), 0f));
            var saySmallViseme = salsa.visemes[0].expData; // cache the say small viseme...

            // Create an expression component for the say small viseme...
            // NOTE: when a viseme is created, the first component is automatically created
            // and default values are used. The default control type is automatically set
            // to shape (blendshape)...so we need to manually set it to Sprite (or whichever
            // type is being used)...
            saySmallViseme.components[0].name = "saySmall component";
            saySmallViseme.components[0].controlType = ExpressionComponent.ControlType.Sprite;
            saySmallViseme.components[0].durationOn = 0.08f; // set to whatever timing you require...
            saySmallViseme.components[0].durationOff = 0.08f; // set to whatever timing you require...
            saySmallViseme.controllerVars[0].spriteRenderer = controller;
            saySmallViseme.controllerVars[0].sprites = sprites;
            // the following are defaults and should be set accordingly.
            saySmallViseme.controllerVars[0].onState = Switcher.OnState.OnUntilOff;
            saySmallViseme.controllerVars[0].display2dImage = false;
            saySmallViseme.controllerVars[0].isRestNull = false;

            // setup viseme 2 -- sayMedium
            salsa.visemes.Add(new LipsyncExpression("sayMedium", new InspectorControllerHelperData(), 0f));
            var sayMediumViseme = salsa.visemes[1].expData; // cache the say medium viseme...
            // create an expression component for the sayMedium viseme...
            sayMediumViseme.components[0].name = "sayMedium component";
            sayMediumViseme.components[0].controlType = ExpressionComponent.ControlType.Sprite;
            sayMediumViseme.components[0].durationOn = 0.08f; // set to whatever timing you require...
            sayMediumViseme.components[0].durationOff = 0.08f; // set to whatever timing you require...
            sayMediumViseme.controllerVars[0].spriteRenderer = controller;
            sprites.Add(new Sprite()); // programmatically add your sprite...
            sprites.Add(new Sprite()); // programmatically add your sprite...
            sayMediumViseme.controllerVars[0].sprites = sprites; // should be different sprites probably...
            // the following are defaults and should be set accordingly...presented
            // here for clarification and options...
            sayMediumViseme.controllerVars[0].onState = Switcher.OnState.OnUntilOff;
            sayMediumViseme.controllerVars[0].display2dImage = false;
            sayMediumViseme.controllerVars[0].isRestNull = false;

            // apply api trigger distribution...
            // at runtime: apply controller baking...

Test Your Runtime Code...

This isn't runtime, but you can use it to quickly test to see if your setup code (above) works (at design time) by calling an editor function...something like the following...

Select the GameObject in your scene with a SpriteRenderer and then select the Unity menu item: GameObject > DemoCode > SALSA LipSync > DemoCode Salsa 2D Runtime Config Call (or whatever you have configured the menu item to be in the below script).

NOTE: The following script is implemented to work with the above runtime code...edit it if you have renamed your class and method above.

using UnityEditor;
using UnityEngine;
using CrazyMinnow.SALSA; // used to setup the audio clip.
using CrazyMinnow.SALSA.OneClicks; // only used to fetch the promo audio clip location...

namespace DemoCode // change this to your custom namespace...
    public class Salsa2DRuntimeTester : Editor
        [MenuItem("GameObject/DemoCode/SALSA LipSync/DemoCode Salsa 2D Runtime Config Call")]
        public static void SetupSalsa2D()
            var selectedObject = Selection.activeGameObject;
            // NOTE: TEST_Salsa2DSpriteRuntime is in the DemoCode namespace by default...

            // An example of loading the demo promo audio clip into the audio source...
            // NOTE: In the above script, the AudioSource was set to not 'play on awake', so
            // be sure to play the audio source when you play your scene. See AudioSource
            // configuration in the BoxheadSalsaRuntime code above.
            var salsa = selectedObject.GetComponent<Salsa>();
            if (salsa != null && salsa.audioSrc != null)
                salsa.audioSrc.clip = AssetDatabase.LoadAssetAtPath<AudioClip>(OneClickBase.RESOURCE_CLIP);