public static int FindBlendSystems(BlendSystemUser component)
    {
        blendSystems     = new List <Type>();
        blendSystemNames = new List <string>();

        blendSystems.Add(null);
        blendSystemNames.Add("None");

        foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
        {
            foreach (Type t in a.GetTypes())
            {
                if (t.IsSubclassOf(typeof(BlendSystem)))
                {
                    blendSystems.Add(t);
                    blendSystemNames.Add(LipSyncEditorExtensions.AddSpaces(t.Name));
                }
            }
        }

        if (component.blendSystem != null)
        {
            return(blendSystems.IndexOf(component.blendSystem.GetType()));
        }

        return(0);
    }
예제 #2
0
    void OnGUI()
    {
        if (emotionsList == null)
        {
            Setup();
        }

        if (centeredStyle == null)
        {
            centeredStyle           = new GUIStyle(EditorStyles.whiteLabel);
            centeredStyle.alignment = TextAnchor.MiddleCenter;
        }

        EditorGUI.BeginChangeCheck();
        GUILayout.Space(10);
        emotionsList.DoLayoutList();
        if (EditorGUI.EndChangeCheck())
        {
            setup.changed          = true;
            setup.previewOutOfDate = true;
        }
        GUILayout.Space(10);
        LipSyncEditorExtensions.BeginPaddedHorizontal(20);
        if (GUILayout.Button("Done", GUILayout.MaxWidth(200), GUILayout.Height(30)))
        {
            Close();
        }
        LipSyncEditorExtensions.EndPaddedHorizontal(20);
    }
예제 #3
0
    void OnGUI()
    {
        if (emotionsList == null)
        {
            Setup();
        }

        if (centeredStyle == null)
        {
            centeredStyle           = new GUIStyle(EditorStyles.whiteLabel);
            centeredStyle.alignment = TextAnchor.MiddleCenter;
        }
        GUILayout.Space(10);
        var oldMixingMode = mixer.mixingMode;

        mixer.mixingMode = (EmotionMixer.MixingMode)EditorGUILayout.EnumPopup("Mixing Mode", mixer.mixingMode);
        if (oldMixingMode != mixer.mixingMode)
        {
            if (mixer.mixingMode == EmotionMixer.MixingMode.Normal && oldMixingMode != EmotionMixer.MixingMode.Normal)
            {
                if (EditorUtility.DisplayDialog("Reset Mixer Values?", "Switching to 'Normal' mixing mode will reset emotion percentages.", "Reset", "Cancel"))
                {
                    for (int i = 0; i < mixer.emotions.Count; i++)
                    {
                        var em = mixer.emotions[i];
                        em.weight         = 1f / mixer.emotions.Count;
                        mixer.emotions[i] = em;
                    }
                }
                else
                {
                    mixer.mixingMode = oldMixingMode;
                }
            }
            else if (mixer.mixingMode == EmotionMixer.MixingMode.Additive && oldMixingMode != EmotionMixer.MixingMode.Additive)
            {
                if (!EditorUtility.DisplayDialog("Unlock Mixer Values?", "Switching to 'Additive' mixing mode will unlock emotion percentages so they can equal more than 100%. You will not be able to switch back to 'Normal' mode without losing these percentages.", "Unlock", "Cancel"))
                {
                    mixer.mixingMode = oldMixingMode;
                }
            }
        }
        EditorGUI.BeginChangeCheck();
        GUILayout.Space(10);
        emotionsList.DoLayoutList();
        if (EditorGUI.EndChangeCheck())
        {
            setup.changed          = true;
            setup.previewOutOfDate = true;
        }
        GUILayout.Space(10);
        LipSyncEditorExtensions.BeginPaddedHorizontal(20);
        if (GUILayout.Button("Done", GUILayout.MaxWidth(200), GUILayout.Height(30)))
        {
            Close();
        }
        LipSyncEditorExtensions.EndPaddedHorizontal(20);
    }
예제 #4
0
    public static void ShowWindow(LipSync component, AnimatorController controller)
    {
        GestureSetupWizard window = EditorWindow.GetWindow <GestureSetupWizard>(true);

        window.component  = component;
        window.controller = controller;
        window.topMessage = "Setting up Gestures for " + controller.name + ".";
        window.totalSteps = 2;
        window.Focus();
        window.titleContent = new GUIContent("Gesture Setup Wizard");

        window.settings = LipSyncEditorExtensions.GetProjectFile();
    }
예제 #5
0
    public override void OnInteractivePreviewGUI(Rect r, GUIStyle background)
    {
        if (lsdTarget.length == 0)
        {
            lsdTarget.length = lsdTarget.clip.length;
        }
#if UNITY_5_3_6 || UNITY_5_4_OR_NEWER
        if (lsdTarget.clip != null)
        {
            AudioUtility.DrawWaveForm(lsdTarget.clip, 0, new Rect(0, r.y + 3, EditorGUIUtility.currentViewWidth, r.height));
        }
#else
        if (waveform == null && lsdTarget.clip != null)
        {
            waveform = AudioUtility.GetWaveForm(lsdTarget.clip, 0, EditorGUIUtility.currentViewWidth, 128);
        }
        if (lsdTarget.clip != null && waveform != null)
        {
            GUI.DrawTexture(new Rect(0, r.y + 3, EditorGUIUtility.currentViewWidth, r.height), waveform);
        }
#endif
        //Playhead
        if (Event.current.button != 1)
        {
            seekPosition = GUI.HorizontalSlider(new Rect(0, r.y + 3, EditorGUIUtility.currentViewWidth, r.height), seekPosition, 0, 1, GUIStyle.none, GUIStyle.none);
        }

        GUI.DrawTexture(new Rect((seekPosition * EditorGUIUtility.currentViewWidth) - 3, r.y, 7, r.height), playhead_line);
        GUI.DrawTexture(new Rect((seekPosition * EditorGUIUtility.currentViewWidth) - 7, r.y, 15, 15), playhead_top);
        GUI.DrawTexture(new Rect((seekPosition * EditorGUIUtility.currentViewWidth) - 7, r.y + r.height - 16, 15, 15), playhead_bottom);

        if (Event.current.type == EventType.Repaint)
        {
            LipSyncEditorExtensions.DrawTimeline(r.y, 0, lsdTarget.length, r.width);
        }

        if (visualPreview && previewTarget != null)
        {
            EditorGUI.HelpBox(new Rect(20, r.y + r.height - 45, r.width - 40, 25), "Preview mode active. Note: only Phonemes and Emotions will be shown in the preview.", MessageType.Info);
        }
        else if (previewTarget != null)
        {
            UpdatePreview(0);
            previewTarget = null;
        }
    }
예제 #6
0
    void FindBlendSystems()
    {
        blendSystems     = new List <System.Type>();
        blendSystemNames = new List <string>();

        blendSystems.Add(null);
        blendSystemNames.Add("None");

        foreach (System.Type t in typeof(BlendshapeBlendSystem).Assembly.GetTypes())
        {
            if (t.IsSubclassOf(typeof(BlendSystem)))
            {
                blendSystems.Add(t);
                blendSystemNames.Add(LipSyncEditorExtensions.AddSpaces(t.Name));
            }
        }

        if (lsTarget.blendSystem != null)
        {
            blendSystemNumber = blendSystems.IndexOf(lsTarget.blendSystem.GetType());
        }
    }
        public static ASMontrealLanguageModel Load(int index)
        {
            string[] languageModelGUIDs = AssetDatabase.FindAssets("t:ASMontrealLanguageModel");

            var settings = LipSyncEditorExtensions.GetProjectFile();

            if (settings == null)
            {
                return(null);
            }
            if (settings.phonemeSet == null)
            {
                return(null);
            }

            if (languageModelGUIDs.Length > 0 && languageModelGUIDs.Length > index)
            {
                ASMontrealLanguageModel model = AssetDatabase.LoadAssetAtPath <ASMontrealLanguageModel>(AssetDatabase.GUIDToAssetPath(languageModelGUIDs[index]));
                if (model != null)
                {
                    if (model.mappingMode == AutoSyncPhonemeMap.MappingMode.InternalMap && !string.IsNullOrEmpty(model.recommendedPhonemeSet) && model.recommendedPhonemeSet != settings.phonemeSet.scriptingName)
                    {
                        if (!EditorUtility.DisplayDialog("Wrong Phoneme Set", "Warning: You are using the '" + settings.phonemeSet.scriptingName + "' Phoneme Set, and this language model is designed for use with '" + model.recommendedPhonemeSet + "'. This may not provide usable results, are you sure you want to continue?", "Yes", "No"))
                        {
                            return(null);
                        }
                    }
                    return(model);
                }
            }
            else
            {
                Debug.LogError("LipSync: Invalid Montreal language model index provided.");
            }

            return(null);
        }
        private List <PhonemeMarker> ParseOutput(string[] lines, ASPocketSphinxLanguageModel lm, AudioClip clip)
        {
            List <PhonemeMarker> results = new List <PhonemeMarker>();

            Dictionary <string, string> phonemeMapper = null;

            var  settings     = LipSyncEditorExtensions.GetProjectFile();
            bool needsMapping = true;

            if (lm.sourcePhoneticAlphabetName == settings.phonemeSet.scriptingName)
            {
                needsMapping = false;
            }
            else
            {
                needsMapping = true;
                switch (lm.mappingMode)
                {
                case AutoSyncPhonemeMap.MappingMode.InternalMap:
                    phonemeMapper = lm.phonemeMap.GenerateAtoBDictionary();
                    break;

                case AutoSyncPhonemeMap.MappingMode.ExternalMap:
                    if (lm.externalMap)
                    {
                        phonemeMapper = lm.externalMap.phonemeMap.GenerateAtoBDictionary();
                    }
                    else
                    {
                        Debug.LogError("Language Model specifies an external phoneme map, but no phoneme map was provided.");
                        return(null);
                    }
                    break;

                default:
                case AutoSyncPhonemeMap.MappingMode.AutoDetect:
                    phonemeMapper = AutoSyncUtility.FindBestFitPhonemeMap(lm.sourcePhoneticAlphabetName, settings.phonemeSet.scriptingName);
                    if (phonemeMapper == null)
                    {
                        Debug.LogErrorFormat("No PhonemeMap could be found to map from '{0}' to the current PhonemeSet '{1}'.", lm.sourcePhoneticAlphabetName, settings.phonemeSet.scriptingName);
                        return(null);
                    }
                    break;
                }

                if (phonemeMapper.Count == 0)
                {
                    Debug.LogWarning("PhonemeMap is empty - this may be due to the language model's mapping mode being set to 'InternalMap' but with no entries being added to the map. Phonemes may not be generated.");
                }
            }

            NumberStyles style   = NumberStyles.Number;
            CultureInfo  culture = CultureInfo.InvariantCulture;

            foreach (string line in lines)
            {
                if (string.IsNullOrEmpty(line))
                {
                    break;
                }
                string[] tokens = line.Split(' ');

                try
                {
                    if (tokens[0] != "SIL")
                    {
                        string phonemeName = needsMapping ? phonemeMapper[tokens[0]] : tokens[0];
                        float  startTime   = -1;
                        float.TryParse(tokens[1], style, culture, out startTime);

                        if (startTime > -1)
                        {
                            startTime /= clip.length;
                        }
                        else
                        {
                            Debug.LogWarning("Phoneme mapper returned invalid timestamp. Skipping this entry.");
                            continue;
                        }

                        bool found = false;
                        int  phoneme;
                        for (phoneme = 0; phoneme < settings.phonemeSet.phonemes.Length; phoneme++)
                        {
                            if (settings.phonemeSet.phonemes[phoneme].name == phonemeName)
                            {
                                found = true;
                                break;
                            }
                        }

                        if (found)
                        {
                            results.Add(new PhonemeMarker(phoneme, startTime));
                        }
                        else
                        {
                            Debug.LogWarning("Phoneme mapper returned '" + phonemeName + "' but this phoneme does not exist in the current set. Skipping this entry.");
                        }
                    }
                }
                catch (ArgumentOutOfRangeException)
                {
                    Debug.LogWarning("Phoneme Label missing from return data. Skipping this entry.");
                }
                catch (KeyNotFoundException)
                {
                    Debug.LogWarning("Phoneme Label '" + tokens[0] + "' not found in phoneme mapper. Skipping this entry.");
                }
            }

            return(results);
        }
예제 #9
0
    private void FinishedProcessingMulti(LipSyncData outputData, AutoSync.ASProcessDelegateData data)
    {
        if (data.success)
        {
            var settings = LipSyncEditorExtensions.GetProjectFile();

            // Create File
            string outputPath = AssetDatabase.GetAssetPath(outputData.clip);
            outputPath = Path.ChangeExtension(outputPath, xmlMode ? "xml" : "asset");

            try
            {
                LipSyncClipSetup.SaveFile(settings, outputPath, xmlMode, outputData.transcript, outputData.length, outputData.phonemeData, outputData.emotionData,
                                          outputData.gestureData, outputData.clip);
            }
            catch (Exception e)
            {
                Debug.LogError(e.StackTrace);
            }
        }
        else
        {
            batchIncomplete = true;
            string clipName = "Undefined";
            if (outputData.clip)
            {
                clipName = outputData.clip.name;
            }

            Debug.LogErrorFormat("AutoSync: Processing failed on clip '{0}'. Continuing with batch.", clipName);
        }

        if (currentClip < clips.Count)
        {
            currentClip++;

            if (autoSyncInstance == null)
            {
                autoSyncInstance = new AutoSync();
            }

            LipSyncData tempData = CreateInstance <LipSyncData>();
            tempData.clip   = clips[currentClip];
            tempData.length = tempData.clip.length;

            if (loadTranscripts)
            {
                tempData.transcript = AutoSyncUtility.TryGetTranscript(tempData.clip);
            }

            autoSyncInstance.RunSequence(currentModules.ToArray(), FinishedProcessingMulti, tempData);
        }
        else
        {
            AssetDatabase.Refresh();
            EditorUtility.ClearProgressBar();

            if (!batchIncomplete)
            {
                setup.ShowNotification(new GUIContent("Batch AutoSync Completed Successfully"));
            }
            else
            {
                setup.ShowNotification(new GUIContent("Batch AutoSync Completed With Errors"));
            }

            Close();
        }
    }
예제 #10
0
        public override void Process(LipSyncData inputClip, AutoSync.ASProcessDelegate callback)
        {
            string mfaPath   = EditorPrefs.GetString("as_montrealfa_application_path");
            string audioPath = AssetDatabase.GetAssetPath(inputClip.clip).Substring("/Assets".Length);

            if (audioPath == null)
            {
                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, "Audio path could not be found.", ClipFeatures.None));
                return;
            }

            if (!AutoSyncUtility.VerifyProgramAtPath(mfaPath, "align"))
            {
                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, "Montreal Forced Aligner application path is not verified.", ClipFeatures.None));
                return;
            }

            // Get absolute path
            audioPath = Application.dataPath + "/" + audioPath;

            // Check Path
            if (audioPath.IndexOfAny(Path.GetInvalidPathChars()) >= 0 || Path.GetFileNameWithoutExtension(audioPath).IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
            {
                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, "Audio path contains invalid characters.", ClipFeatures.None));
                return;
            }

            // Load Language Model
            ASMontrealLanguageModel model = ASMontrealLanguageModel.Load(languageModel);

            if (model == null)
            {
                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, "Language Model failed to load.", ClipFeatures.None));
                return;
            }

            string basePath    = model.GetBasePath();
            string lexiconPath = "";

            if (model.usePredefinedLexicon)
            {
                lexiconPath = basePath + model.lexiconPath;
            }
            else
            {
                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, "Support for generated lexicons using a G2P model is coming soon.", ClipFeatures.None));
                return;
            }

            string adjustedName = Path.GetFileNameWithoutExtension(audioPath).Replace('.', '_').Replace(' ', '_').Replace('\\', '_').Replace('/', '_');

            string corpusPath = Application.temporaryCachePath + "/" + adjustedName + "_MFA_Corpus";
            string outputPath = Application.temporaryCachePath + "/" + adjustedName + "_MFA_Output";

            // Delete folders if they already exist
            try
            {
                if (Directory.Exists(corpusPath))
                {
                    Directory.Delete(corpusPath, true);
                }
                if (Directory.Exists(outputPath))
                {
                    Directory.Delete(outputPath, true);
                }
            }
            catch
            {
                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, "Attempt to clear temporary MFA folders failed. Are they open in another application?", ClipFeatures.None));
                return;
            }

            // Create temporary folders
            Directory.CreateDirectory(corpusPath);
            Directory.CreateDirectory(outputPath);

            // Copy or convert audio clip to corpus folder
            if (AutoSyncConversionUtility.IsConversionAvailable && useAudioConversion)
            {
                AutoSyncConversionUtility.StartConversion(audioPath, corpusPath + "/" + adjustedName + ".wav", AutoSyncConversionUtility.AudioFormat.WavPCM, 16000, 16, 1);
            }
            else
            {
                File.Copy(audioPath, corpusPath + "/" + adjustedName + Path.GetExtension(audioPath));
            }

            // Create transcript file in corpus folder
            StreamWriter transcriptWriter = File.CreateText(corpusPath + "/" + adjustedName + ".lab");

            transcriptWriter.Write(inputClip.transcript.Replace('-', ' '));
            transcriptWriter.Close();

            // Run aligner
            Directory.SetCurrentDirectory(Application.dataPath.Remove(Application.dataPath.Length - 6));
            mfaPath = Path.GetFullPath(mfaPath);

            System.Diagnostics.Process process = new System.Diagnostics.Process();
            process.StartInfo.FileName = mfaPath;

#if UNITY_EDITOR_WIN
            process.StartInfo.Arguments = "\"" + corpusPath + "\" \"" + lexiconPath + "\" \"" + basePath + model.acousticModelPath + "\" \"" + outputPath + "\" --quiet";
#elif UNITY_EDITOR_OSX
            process.StartInfo.Arguments = "\"" + corpusPath + "\" \"" + lexiconPath + "\" \"" + basePath + model.acousticModelPath + "\" \"" + outputPath + "\"";
#endif
            process.StartInfo.UseShellExecute = true;
            process.StartInfo.CreateNoWindow  = true;

            process.Start();
            process.WaitForExit(15000);

            if (!process.HasExited)
            {
                process.Kill();
                process.Close();
            }

            var           outputFiles  = Directory.GetFiles(outputPath, "*", SearchOption.AllDirectories);
            string        textGridPath = "";
            List <string> oovs         = new List <string>();
            bool          nothingFound = true;

            for (int i = 0; i < outputFiles.Length; i++)
            {
                if (Path.GetExtension(outputFiles[i]).ToLowerInvariant() == ".textgrid")
                {
                    textGridPath = outputFiles[i];
                    nothingFound = false;
                }
                else if (Path.GetExtension(outputFiles[i]).ToLowerInvariant() == ".txt")
                {
                    string name   = Path.GetFileNameWithoutExtension(outputFiles[i]);
                    var    reader = new StreamReader(outputFiles[i]);
                    if (name == "oovs_found")
                    {
                        while (!reader.EndOfStream)
                        {
                            oovs.Add(reader.ReadLine());
                        }
                    }
                    reader.Close();
                }
            }

            // Detect out-of-vocab words, filter and retry if enabled.
            if (oovs.Count > 0)
            {
                Debug.Log("Found out-of-vocabulary words:");
                for (int i = 0; i < oovs.Count; i++)
                {
                    Debug.Log(oovs[i]);
                }
            }

            if (nothingFound)
            {
                if (autoRetry)
                {
                    if (attempts < maxAttempts - 1)
                    {
                        attempts++;
                        Process(inputClip, callback);
                        return;
                    }
                }

                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, "MFA Application Failed. Check your audio encoding or enable conversion.", ClipFeatures.None));
                return;
            }

            // Load in TextGrid
            TextGridUtility.TextGridItem[] items = TextGridUtility.ParseTextGridFile(textGridPath);

            var  settings     = LipSyncEditorExtensions.GetProjectFile();
            bool needsMapping = true;
            Dictionary <string, string> phonemeMapper = null;

            if (model.sourcePhoneticAlphabetName == settings.phonemeSet.scriptingName)
            {
                needsMapping = false;
            }
            else
            {
                needsMapping = true;
                switch (model.mappingMode)
                {
                case AutoSyncPhonemeMap.MappingMode.InternalMap:
                    phonemeMapper = model.phonemeMap.GenerateAtoBDictionary();
                    break;

                case AutoSyncPhonemeMap.MappingMode.ExternalMap:
                    if (model.externalMap)
                    {
                        phonemeMapper = model.externalMap.phonemeMap.GenerateAtoBDictionary();
                    }
                    else
                    {
                        callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, "Language Model specifies an external phoneme map, but no phoneme map was provided.", ClipFeatures.None));
                        return;
                    }
                    break;

                default:
                case AutoSyncPhonemeMap.MappingMode.AutoDetect:
                    phonemeMapper = AutoSyncUtility.FindBestFitPhonemeMap(model.sourcePhoneticAlphabetName, settings.phonemeSet.scriptingName);
                    if (phonemeMapper == null)
                    {
                        callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, string.Format("No PhonemeMap could be found to map from '{0}' to the current PhonemeSet '{1}'.", model.sourcePhoneticAlphabetName, settings.phonemeSet.scriptingName), ClipFeatures.None));
                        return;
                    }
                    break;
                }

                if (phonemeMapper.Count == 0)
                {
                    Debug.LogWarning("PhonemeMap is empty - this may be due to the language model's mapping mode being set to 'InternalMap' but with no entries being added to the map. Phonemes may not be generated.");
                }
            }

            // Get Phones
            List <PhonemeMarker> data = new List <PhonemeMarker>();

            if (items != null && items.Length == 2)
            {
                for (int i = 0; i < items[1].intervals.Length; i++)
                {
                    if (items[1].intervals[i] == null)
                    {
                        Debug.LogFormat("Interval {0} is null :o", i);
                        continue;
                    }

                    string label = items[1].intervals[i].text.Split('"')[1];
                    label = System.Text.RegularExpressions.Regex.Replace(label, "[0-9]", "");

                    if (label != "sil")
                    {
                        if (phonemeMapper.ContainsKey(label))
                        {
                            string phonemeName = needsMapping ? phonemeMapper[label] : label;

                            bool found = false;
                            int  phoneme;
                            for (phoneme = 0; phoneme < settings.phonemeSet.phonemes.Length; phoneme++)
                            {
                                if (settings.phonemeSet.phonemes[phoneme].name == phonemeName)
                                {
                                    found = true;
                                    break;
                                }
                            }

                            if (found)
                            {
                                double start = items[1].intervals[i].xmin / inputClip.length;
                                double end   = items[1].intervals[i].xmax / inputClip.length;

                                double length = end - start;
                                if ((length * inputClip.length) < minLengthForSustain)
                                {
                                    data.Add(new PhonemeMarker(phoneme, (float)(start + (length / 2))));
                                }
                                else
                                {
                                    data.Add(new PhonemeMarker(phoneme, (float)start, 1, true));
                                    data.Add(new PhonemeMarker(phoneme, (float)end));
                                }
                            }
                            else
                            {
                                Debug.LogWarning("Phoneme mapper returned '" + phonemeName + "' but this phoneme does not exist in the current set. Skipping this entry.");
                            }
                        }
                        else
                        {
                            Debug.LogWarning("Phoneme mapper does not contain '" + label + "' Skipping this entry.");
                        }
                    }
                }
            }
            else
            {
                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(false, "Data loaded from MFA TextGrid file was invalid or incomplete.", ClipFeatures.None));
                return;
            }


            inputClip.phonemeData = data.ToArray();

            if (oovs.Count > 0)
            {
                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(true, "Completed, but some words were not found. Check the console.", GetOutputCompatibility()));
            }
            else
            {
                callback.Invoke(inputClip, new AutoSync.ASProcessDelegateData(true, "", GetOutputCompatibility()));
            }

            return;
        }
 public override void DrawSetupWizardSection()
 {
     EditorGUILayout.HelpBox("The Montreal Forced Aligner contains multiple executables, the one required below is \"bin/mfa_align.exe\" on Windows, or \"lib/align\" on macOS - used to align phonemes with audio.", MessageType.Info);
     applicationPath = LipSyncEditorExtensions.DrawPathField("MFA Aligner Application Path", applicationPath, "ls_debug", "Find mfa_align executable");
 }
    public override void OnInspectorGUI()
    {
        // Create styles if necesarry
        if (inlineToolbar == null)
        {
            inlineToolbar             = new GUIStyle((GUIStyle)"TE toolbarbutton");
            inlineToolbar.fixedHeight = 0;
            inlineToolbar.fixedWidth  = 0;
        }

        LipSyncEditorExtensions.BeginPaddedHorizontal();
        GUILayout.Box(logo, GUIStyle.none);
        LipSyncEditorExtensions.EndPaddedHorizontal();

        GUILayout.Space(20);

        serializedObject.Update();

        Rect lineRect;

        EditorGUILayout.HelpBox("Enable or disable Eye Controller functionality below.", MessageType.Info);

        GUILayout.Space(10);

        blendSystemNumber = BlendSystemEditor.DrawBlendSystemEditor(myTarget, blendSystemNumber, "EyeController requires a blend system to function.");

        if (myTarget.blendSystem != null)
        {
            if (myTarget.blendSystem.isReady)
            {
                if (blendables == null)
                {
                    myTarget.blendSystem.onBlendablesChanged += GetBlendShapes;
                    GetBlendShapes();
                }

                EditorGUILayout.PropertyField(boneUpdateAnimation, new GUIContent("Account for Animation", "If true, will calculate relative bone positions/rotations each frame. Improves results when using animation, but will cause errors when not."));
                GUILayout.Space(10);
                BlendSystemEditor.DrawBlendSystemButtons(myTarget.blendSystem);
                GUILayout.Space(10);

                // Blinking
                lineRect = EditorGUILayout.BeginHorizontal();
                GUI.Box(lineRect, "", (GUIStyle)"flow node 0");
                GUILayout.Space(10);
                blinkingEnabled.boolValue = EditorGUILayout.ToggleLeft("Blinking", blinkingEnabled.boolValue, EditorStyles.largeLabel, GUILayout.ExpandWidth(true), GUILayout.Height(24));
                showBlinking.target       = blinkingEnabled.boolValue;
                GUILayout.FlexibleSpace();
                blinkingControlMode.enumValueIndex = GUILayout.Toolbar(blinkingControlMode.enumValueIndex, System.Enum.GetNames(typeof(EyeController.ControlMode)), inlineToolbar, GUILayout.MaxWidth(300), GUILayout.Height(24));
                showBlinkingClassicControl.target  = blinkingControlMode.enumValueIndex == (int)EyeController.ControlMode.Classic;
                EditorGUILayout.EndHorizontal();
                EditorGUI.indentLevel++;
                if (EditorGUILayout.BeginFadeGroup(showBlinking.faded))
                {
                    GUILayout.Space(5);

                    // Classic Mode
                    if (EditorGUILayout.BeginFadeGroup(showBlinkingClassicControl.faded))
                    {
                        if (blendables != null)
                        {
                            leftEyeBlinkBlendshape.intValue  = EditorGUILayout.Popup("Left Eye Blink Blendshape", leftEyeBlinkBlendshape.intValue, blendables, GUILayout.MaxWidth(500));
                            rightEyeBlinkBlendshape.intValue = EditorGUILayout.Popup("Right Eye Blink Blendshape", rightEyeBlinkBlendshape.intValue, blendables, GUILayout.MaxWidth(500));
                        }
                    }
                    LipSyncEditorExtensions.FixedEndFadeGroup(showBlinkingClassicControl.faded);

                    // Pose Mode
                    if (EditorGUILayout.BeginFadeGroup(1 - showBlinkingClassicControl.faded))
                    {
                        this.DrawShapeEditor(myTarget.blendSystem, blendables, true, true, myTarget.blinkingShape, "Blinking", 0);
                    }
                    LipSyncEditorExtensions.FixedEndFadeGroup(1 - showBlinkingClassicControl.faded);

                    GUILayout.Space(10);

                    float minGap = minimumBlinkGap.floatValue;
                    float maxGap = maximumBlinkGap.floatValue;

                    MinMaxSliderWithNumbers(new GUIContent("Blink Gap", "Time, in seconds, between blinks."), ref minGap, ref maxGap, 0.1f, 20);

                    minimumBlinkGap.floatValue = minGap;
                    maximumBlinkGap.floatValue = maxGap;

                    EditorGUILayout.PropertyField(blinkDuration, new GUIContent("Blink Duration", "How long each blink takes."));
                    GUILayout.Space(10);
                }
                LipSyncEditorExtensions.FixedEndFadeGroup(showBlinking.faded);
                EditorGUI.indentLevel--;
                GUILayout.Space(2);
                // Random Look Direction
                lineRect = EditorGUILayout.BeginHorizontal();
                GUI.Box(lineRect, "", (GUIStyle)"flow node 0");
                GUILayout.Space(10);
                randomLookingEnabled.boolValue = EditorGUILayout.ToggleLeft("Random Looking", randomLookingEnabled.boolValue, EditorStyles.largeLabel, GUILayout.ExpandWidth(true), GUILayout.Height(24));
                showRandomLook.target          = randomLookingEnabled.boolValue;
                GUILayout.FlexibleSpace();
                lookingControlMode.enumValueIndex = GUILayout.Toolbar(lookingControlMode.enumValueIndex, System.Enum.GetNames(typeof(EyeController.ControlMode)), inlineToolbar, GUILayout.MaxWidth(300), GUILayout.Height(24));
                showLookingClassicControl.target  = lookingControlMode.enumValueIndex == (int)EyeController.ControlMode.Classic;
                EditorGUILayout.EndHorizontal();
                EditorGUI.indentLevel++;
                if (EditorGUILayout.BeginFadeGroup(showRandomLook.faded))
                {
                    GUILayout.Space(5);

                    float minGap = minimumChangeDirectionGap.floatValue;
                    float maxGap = maximumChangeDirectionGap.floatValue;

                    MinMaxSliderWithNumbers(new GUIContent("Change Direction Gap", "Time, in seconds, between the eyes turning to a new direction."), ref minGap, ref maxGap, 1f, 30);

                    minimumChangeDirectionGap.floatValue = minGap;
                    maximumChangeDirectionGap.floatValue = maxGap;

                    GUILayout.Space(10);
                }
                LipSyncEditorExtensions.FixedEndFadeGroup(showRandomLook.faded);
                EditorGUI.indentLevel--;

                // Look Targets
                lineRect = EditorGUILayout.BeginHorizontal();
                GUI.Box(lineRect, "", (GUIStyle)"flow node 0");
                GUILayout.Space(10);
                EditorGUI.BeginDisabledGroup(lookingControlMode.enumValueIndex == (int)EyeController.ControlMode.PoseBased);
                targetEnabled.boolValue = EditorGUILayout.ToggleLeft("Look At Target", targetEnabled.boolValue, EditorStyles.largeLabel, GUILayout.ExpandWidth(true), GUILayout.Height(24)) && lookingControlMode.enumValueIndex == (int)EyeController.ControlMode.Classic;
                showLookTarget.target   = targetEnabled.boolValue;
                EditorGUI.EndDisabledGroup();
                GUILayout.FlexibleSpace();
                GUILayout.Space(24);
                EditorGUILayout.EndHorizontal();
                EditorGUI.indentLevel++;
                if (EditorGUILayout.BeginFadeGroup(showLookTarget.faded))
                {
                    GUILayout.Space(5);
                    if (EditorGUILayout.BeginFadeGroup(1 - showAutoTarget.faded))
                    {
                        EditorGUILayout.PropertyField(viewTarget, new GUIContent("Target", "Transform to look at."));
                        GUILayout.Space(10);
                    }
                    LipSyncEditorExtensions.FixedEndFadeGroup(1 - showAutoTarget.faded);
                    EditorGUILayout.PropertyField(autoTarget, new GUIContent("Use Auto Target"));
                    showAutoTarget.target = myTarget.autoTarget;
                    if (EditorGUILayout.BeginFadeGroup(showAutoTarget.faded))
                    {
                        EditorGUI.indentLevel++;
                        EditorGUILayout.PropertyField(autoTargetTag, new GUIContent("Auto Target Tag", "Tag to use when searching for targets."));
                        EditorGUILayout.PropertyField(autoTargetDistance, new GUIContent("Auto Target Distance", "The maximum distance between a target and the character for it to be targeted."));
                        EditorGUI.indentLevel--;
                        GUILayout.Space(10);
                    }
                    LipSyncEditorExtensions.FixedEndFadeGroup(showAutoTarget.faded);
                    EditorGUILayout.Slider(targetWeight, 0, 1, new GUIContent("Look At Amount"));
                    GUILayout.Space(10);
                }
                LipSyncEditorExtensions.FixedEndFadeGroup(showLookTarget.faded);
                if (lookingControlMode.enumValueIndex == (int)EyeController.ControlMode.PoseBased)
                {
                    EditorGUILayout.HelpBox("Targeting is only available in classic mode.", MessageType.Warning);
                }
                EditorGUI.indentLevel--;

                // Shared Look Controls
                GUILayout.Space(-2);
                lineRect = EditorGUILayout.BeginHorizontal();
                GUI.Box(lineRect, "", (GUIStyle)"flow node 0");
                GUILayout.Space(24);
                GUILayout.Label("Looking (Shared)", EditorStyles.largeLabel, GUILayout.ExpandWidth(true), GUILayout.Height(24));
                GUILayout.FlexibleSpace();
                EditorGUILayout.EndHorizontal();
                showLookShared.target = myTarget.targetEnabled || myTarget.randomLookingEnabled;
                EditorGUI.indentLevel++;
                if (EditorGUILayout.BeginFadeGroup(showLookShared.faded))
                {
                    GUILayout.Space(5);

                    // Classic Mode
                    if (EditorGUILayout.BeginFadeGroup(showLookingClassicControl.faded))
                    {
                        EditorGUI.BeginChangeCheck();
                        EditorGUILayout.PropertyField(leftEyeLookAtBone);
                        EditorGUILayout.PropertyField(rightEyeLookAtBone);
                        if (EditorGUI.EndChangeCheck())
                        {
                            myTarget.LeftEyeLookAtBone  = (Transform)leftEyeLookAtBone.objectReferenceValue;
                            myTarget.RightEyeLookAtBone = (Transform)rightEyeLookAtBone.objectReferenceValue;
                        }
                        GUILayout.Space(5);
                        MinMaxSliderWithNumbers(new GUIContent("X Axis Range"), eyeRotationRangeX, -90, 90);
                        MinMaxSliderWithNumbers(new GUIContent("Y Axis Range"), eyeRotationRangeY, -90, 90);
                        GUILayout.Space(5);
                        EditorGUILayout.PropertyField(eyeLookOffset);
                        EditorGUILayout.PropertyField(eyeForwardAxis);
                    }
                    LipSyncEditorExtensions.FixedEndFadeGroup(showLookingClassicControl.faded);

                    // Pose Mode
                    if (EditorGUILayout.BeginFadeGroup(1 - showLookingClassicControl.faded))
                    {
                        this.DrawShapeEditor(myTarget.blendSystem, blendables, true, true, myTarget.lookingUpShape, "Looking Up", 1);
                        this.DrawShapeEditor(myTarget.blendSystem, blendables, true, true, myTarget.lookingDownShape, "Looking Down", 2);
                        this.DrawShapeEditor(myTarget.blendSystem, blendables, true, true, myTarget.lookingLeftShape, "Looking Left", 3);
                        this.DrawShapeEditor(myTarget.blendSystem, blendables, true, true, myTarget.lookingRightShape, "Looking Right", 4);
                    }
                    LipSyncEditorExtensions.FixedEndFadeGroup(1 - showLookingClassicControl.faded);
                    GUILayout.Space(5);
                    EditorGUILayout.PropertyField(eyeTurnSpeed, new GUIContent("Eye Turn Speed", "The speed at which eyes rotate."));
                    GUILayout.Space(10);
                }
                LipSyncEditorExtensions.FixedEndFadeGroup(showLookShared.faded);
                EditorGUI.indentLevel--;
                GUILayout.Space(10);
            }

            if (LipSyncEditorExtensions.oldToggle != LipSyncEditorExtensions.currentToggle && LipSyncEditorExtensions.currentTarget == myTarget)
            {
                Shape oldShape = null;
                Shape newShape = null;

                switch (LipSyncEditorExtensions.oldToggle)
                {
                case 0:
                    oldShape = myTarget.blinkingShape;
                    break;

                case 1:
                    oldShape = myTarget.lookingUpShape;
                    break;

                case 2:
                    oldShape = myTarget.lookingDownShape;
                    break;

                case 3:
                    oldShape = myTarget.lookingLeftShape;
                    break;

                case 4:
                    oldShape = myTarget.lookingRightShape;
                    break;
                }

                switch (LipSyncEditorExtensions.currentToggle)
                {
                case 0:
                    newShape = myTarget.blinkingShape;
                    break;

                case 1:
                    newShape = myTarget.lookingUpShape;
                    break;

                case 2:
                    newShape = myTarget.lookingDownShape;
                    break;

                case 3:
                    newShape = myTarget.lookingLeftShape;
                    break;

                case 4:
                    newShape = myTarget.lookingRightShape;
                    break;
                }

                if (LipSyncEditorExtensions.oldToggle > -1)
                {
                    if (oldShape != null)
                    {
                        foreach (BoneShape boneshape in oldShape.bones)
                        {
                            if (boneshape.bone != null)
                            {
                                boneshape.bone.localPosition    = boneshape.neutralPosition;
                                boneshape.bone.localEulerAngles = boneshape.neutralRotation;
                            }
                        }

                        foreach (int blendable in oldShape.blendShapes)
                        {
                            myTarget.blendSystem.SetBlendableValue(blendable, 0);
                        }
                    }
                }

                if (LipSyncEditorExtensions.currentToggle > -1)
                {
                    foreach (BoneShape boneshape in newShape.bones)
                    {
                        if (boneshape.bone != null)
                        {
                            boneshape.bone.localPosition    = boneshape.endPosition;
                            boneshape.bone.localEulerAngles = boneshape.endRotation;
                        }
                    }

                    for (int b = 0; b < newShape.blendShapes.Count; b++)
                    {
                        myTarget.blendSystem.SetBlendableValue(newShape.blendShapes[b], newShape.weights[b]);
                    }
                }

                LipSyncEditorExtensions.oldToggle = LipSyncEditorExtensions.currentToggle;
            }

            if (GUI.changed)
            {
                if (blendables == null)
                {
                    GetBlendShapes();
                }

                Shape newShape = null;

                switch (LipSyncEditorExtensions.currentToggle)
                {
                case 0:
                    newShape = myTarget.blinkingShape;
                    break;

                case 1:
                    newShape = myTarget.lookingUpShape;
                    break;

                case 2:
                    newShape = myTarget.lookingDownShape;
                    break;

                case 3:
                    newShape = myTarget.lookingLeftShape;
                    break;

                case 4:
                    newShape = myTarget.lookingRightShape;
                    break;
                }

                if (LipSyncEditorExtensions.currentToggle > -1 && LipSyncEditorExtensions.currentTarget == myTarget)
                {
                    for (int b = 0; b < newShape.blendShapes.Count; b++)
                    {
                        myTarget.blendSystem.SetBlendableValue(newShape.blendShapes[b], newShape.weights[b]);
                    }
                }

                EditorUtility.SetDirty(myTarget);
                serializedObject.SetIsDifferentCacheDirty();
            }

            serializedObject.ApplyModifiedProperties();
        }
    }
    public static void ShowWindow()
    {
        var settings = LipSyncEditorExtensions.GetProjectFile();

        Selection.activeObject = settings;
    }
예제 #14
0
 public override void OnInspectorGUI()
 {
     serializedObject.Update();
     LipSyncEditorExtensions.DrawVerifiedPathField(serializedObject.FindProperty("applicationPath"), "as_montrealfa_application_path", "as_montrealfa_application_path_verified", "Find Montreal Forced Aligner Application", Verify);
     serializedObject.ApplyModifiedProperties();
 }