// - Don't upload model data, motion data, this code in github or public space without permission. using UnityEngine; using System.Collections; using System.Collections.Generic; public class MMD4MecanimSpeechHelper : MMD4MecanimMorphHelper { public AudioClip speechAudioClip; public string speechMorphText; public float elementLength = 0.0f; public float consonantLength = 0.0f; static readonly float defaultElementLength = 0.2f; static readonly float defaultConsonantLength = 0.1f; public float GetElementLength() { return( elementLength > 0.0f ) ? elementLength : defaultElementLength; } public float GetConsonantLength() { return( consonantLength > 0.0f ) ? consonantLength : defaultConsonantLength; } static List hiraMorphNameList = new List() { "\u3042", // Hira a "\u3044", // Hira i "\u3046", // Hira u "\u3048", // Hira e "\u304A", // Hira o }; // ^ ... alveolar // ` ... bilabial // / ... staccato // - ... - // ~ ... - static bool _IsEscape( char ch ) { return (ch == '^') || (ch == '`') || (ch == '/') || (ch == '-') || (ch == '~'); } // 2 or 3 static Dictionary englishPhraseDictionary = new Dictionary() { {"ch", "i"}, // Reach, Speech {"ck", "u"}, // Kick {"cs", "uu"}, // Physics {"dy", "i-"}, // Body {"ff", "u"}, // Staff {"ght", "o"}, // Eight, Knight, Night {"kni","ai"}, // Knight {"llo","o-"}, // Hello {"phy", "i"}, // Physics {"ss", "/u"}, // Lesson {"ty", "i-"}, // Liberty {"th", "u"}, // Three {"ca", "a"}, {"ci", "i"}, {"cu", "u"}, {"ce", "e"}, {"co", "o"}, {"qa", "a"}, {"qi", "i"}, {"qu", "u"}, {"qe", "e"}, {"qo", "o"}, {"ka", "a"}, {"ki", "i"}, {"ku", "u"}, {"ke", "e"}, {"ko", "o"}, {"ga", "a"}, {"gi", "ai"}, {"gu", "u"}, {"ge", "e"}, {"go", "o-"}, {"sa", "^a"}, {"si", "i/"}, {"su", "u"}, {"se", "^u"}, {"so", "^o"}, {"sha","^a"}, {"shi", "i"}, {"shu", "u"}, {"she","^i-"}, {"sho", "^o"}, {"xa", "^a"}, {"xi", "^i"}, {"xu", "^u"}, {"xe", "^e"}, {"xo", "^o"}, {"za", "^a"}, {"zi", "^i"}, {"zu", "^u"}, {"ze", "^e"}, {"zo", "^o"}, {"ja", "^a"}, {"ji", "i"}, {"ju", "u"}, {"je", "^e"}, {"jo", "^o"}, {"ta", "a"}, {"ti", "i"}, {"tu", "u"}, {"te", "^e"}, {"to", "u-"}, {"fa", "^a"}, {"fi", "^i"}, {"fu", "u"}, {"fe", "^e"}, {"fo", "^o"}, {"da", "a"}, {"di", "i"}, {"du", "u"}, {"de", "e"}, {"do", "u-"}, {"na", "^a"}, {"ni", "ai"}, {"nu", "u"}, {"ne", "u"}, {"no", "^o"}, {"nya","^a"}, {"nyi", "i"}, {"nyu", "u"}, {"nye" , "e"}, {"nyo", "^o"}, {"ha", "a"}, {"hi", "ai"}, {"hu", "u"}, {"he", "e"}, {"ho", "o"}, {"pa", "`a"}, {"pi", "`i"}, {"pu", "`u"}, {"pe", "`e"}, {"po", "`o"}, {"ba", "`a"}, {"bi", "`i"}, {"bu", "`u"}, {"be", "i"}, {"bo", "o"}, {"ma", "`a"}, {"mi", "`i"}, {"mu", "`u"}, {"me", "`e"}, {"mo", "`a"}, {"ya", "a"}, {"yi", "i"}, {"yu", "u"}, {"ye", "e"}, {"yo", "o"}, {"ra", "o"}, {"ri", "i"}, {"ru", "a"}, {"re", "i"}, {"ro", "o-"}, {"la", "a"}, {"li", "i"}, {"lu", "u"}, {"le", "i"}, {"lo", "a"}, {"wa", "^a"}, {"wi","^i-"}, {"wu", "u-"}, {"we", "^i-"}, {"wo", "^o-"}, {"va", "`a"}, {"vi", "`i"}, {"vu", "`u"}, {"ve", "`u"}, {"vo", "`a"}, }; static Dictionary englishPronunDictionary = new Dictionary() { {'a', "a"}, {'i', "i"}, {'u', "u"}, {'e', "e"}, {'o', "o"}, {'n', "n"}, {'b', "u"}, {'c', "u"}, {'d', "o"}, {'f', "u"}, {'g', "u"}, {'h', "u"}, {'j', "i"}, {'k', "u"}, {'l', "u"}, {'m', "u"}, {'p', "u"}, {'q', "u"}, {'r', "a"}, {'s', "u"}, {'t', "o"}, {'v', "u"}, {'w', "u"}, {'x',"uu"}, // Six {'y', "i"}, {'z', "u"}, }; static Dictionary japanesePostPhraseDictionary = new Dictionary() { {'\u3041', "a"}, {'\u3043', "i"}, {'\u3045', "u"}, {'\u3047', "e"}, {'\u3049', "o"}, // Hira la/li/lu/le/lo {'\u30A1', "a"}, {'\u30A3', "i"}, {'\u30A5', "u"}, {'\u30A7', "e"}, {'\u30A9', "o"}, // Kana la/li/lu/le/lo }; // 2 static Dictionary japanesePhraseDictionary = new Dictionary() { {"\u304D\u3041", "^a"}, {"\u304D\u3043", "i"}, {"\u304D\u3045", "u"}, {"\u304D\u3047", "^e"}, {"\u304D\u3049", "^o"}, // Hira kya/kyu/kyo {"\u3057\u3041", "^a"}, {"\u3057\u3043", "i"}, {"\u3057\u3045", "u"}, {"\u3057\u3047", "^e"}, {"\u3057\u3049", "^o"}, // Hira sya/shu/sho {"\u306B\u3041", "^a"}, {"\u306B\u3043", "i"}, {"\u306B\u3045", "u"}, {"\u306B\u3047", "^e"}, {"\u306B\u3049", "^o"}, // Hira nya/nyu/nyo {"\u3072\u3041", "^a"}, {"\u3072\u3043", "i"}, {"\u3072\u3045", "u"}, {"\u3072\u3047", "^e"}, {"\u3072\u3049", "^o"}, // Hira hya/hyu/hyo {"\u3073\u3041", "`a"}, {"\u3073\u3043","`i"}, {"\u3073\u3045","`u"}, {"\u3073\u3047", "`e"}, {"\u3073\u3049", "`o"}, // Hira bya/byu/byo {"\u3074\u3041", "`a"}, {"\u3074\u3043","`i"}, {"\u3074\u3045","`u"}, {"\u3074\u3047", "`e"}, {"\u3074\u3049", "`o"}, // Hira pya/pyu/byo {"\u307E\u3041", "`a"}, {"\u307E\u3043","`i"}, {"\u307E\u3045","`u"}, {"\u307E\u3047", "`e"}, {"\u307E\u3049", "`o"}, // Hira mya/myu/myo {"\u308A\u3041", "^a"}, {"\u308A\u3043", "i"}, {"\u308A\u3045", "u"}, {"\u308A\u3047", "^e"}, {"\u308A\u3049", "^o"}, // Hira rya/ryu/ryo {"\u30AD\u30A1", "^a"}, {"\u30AD\u30A3", "i"}, {"\u30AD\u30A5", "u"}, {"\u30AD\u30A7", "^e"}, {"\u30AD\u30A9", "^o"}, // Kana kya/nyu/kyo {"\u30B7\u30A1", "^a"}, {"\u30B7\u30A3", "i"}, {"\u30B7\u30A5", "u"}, {"\u30B7\u30A7", "^e"}, {"\u30B7\u30A9", "^o"}, // Kana sya/syu/syo {"\u30CB\u30A1", "^a"}, {"\u30CB\u30A3", "i"}, {"\u30CB\u30A5", "u"}, {"\u30CB\u30A7", "^e"}, {"\u30CB\u30A9", "^o"}, // Kana nya/nyu/nyo {"\u30D2\u30A1", "`a"}, {"\u30D2\u30A3","`i"}, {"\u30D2\u30A5","`u"}, {"\u30D2\u30A7", "`e"}, {"\u30D2\u30A9", "`o"}, // Kana hya/hyu/hyo {"\u30D3\u30A1", "`a"}, {"\u30D3\u30A3","`i"}, {"\u30D3\u30A5","`u"}, {"\u30D3\u30A7", "`e"}, {"\u30D3\u30A9", "`o"}, // Kana bya/byu/byo {"\u30D4\u30A1", "`a"}, {"\u30D4\u30A3","`i"}, {"\u30D4\u30A5","`u"}, {"\u30D4\u30A7", "`e"}, {"\u30D4\u30A9", "`o"}, // Kana pya/pyu/pyo {"\u30DF\u30A1", "`a"}, {"\u30DF\u30A3","`i"}, {"\u30DF\u30A5","`u"}, {"\u30DF\u30A7", "`e"}, {"\u30DF\u30A9", "`o"}, // Kana mya/myu/myo {"\u30EA\u30A1", "^a"}, {"\u30EA\u30A3", "i"}, {"\u30EA\u30A5", "u"}, {"\u30EA\u30A7", "^e"}, {"\u30EA\u30A9", "^o"}, // Kana rya/ryu/ryo {"\u30F4\u30A1", "`a"}, {"\u30F4\u30A3","`i"}, {"\u30F4\u30A5","`u"}, {"\u30F4\u30A7", "`e"}, {"\u30F4\u30A9", "`o"}, // Kana va/vi/vu/ve/vo }; static Dictionary punctuatDictionary = new Dictionary() { { '.', " " }, { ',', " " }, { '!', " " }, { '?', " " }, {'\uFF0E', " "}, // . {'\uFF0C', " "}, // , {'\uFF01', " "}, // ! {'\uFF1F', " "}, // ? {'\u3001', " "}, // Japanese , {'\u3002', " "}, // Japanese . }; static Dictionary japanesePronunDictionary = new Dictionary() { {'\u30FC', "-"}, {'\u2015', "-"}, {'\u301C', "-"}, {'\u3041', "a"}, {'\u3043', "i"}, {'\u3045', "u"}, {'\u3047', "e"}, {'\u3049', "o"}, // Hira la/li/lu/le/lo {'\u3042', "a"}, {'\u3044', "i"}, {'\u3046', "u"}, {'\u3048', "e"}, {'\u304A', "o"}, // Hira a/i/u/e/o {'\u304B', "a"}, {'\u304D', "i"}, {'\u304F', "u"}, {'\u3051', "e"}, {'\u3053', "o"}, // Hira ka/ki/ku/ke/ko {'\u304C', "a"}, {'\u304E', "i"}, {'\u3050', "u"}, {'\u3052', "e"}, {'\u3054', "o"}, // Hira ga/gi/gu/ge/go {'\u3055', "^a"}, {'\u3057', "i"}, {'\u3059', "u"}, {'\u305B', "^e"}, {'\u305D', "^o"}, // Hira sa/si/su/se/so {'\u3056', "^a"}, {'\u3058', "i"}, {'\u305A', "u"}, {'\u305C', "^e"}, {'\u305E', "^o"}, // Hira za/zi/zu/ze/zo {'\u305F', "^a"}, {'\u3061', "i"}, {'\u3064', "u"}, {'\u3066', "^e"}, {'\u3068', "^o"}, // Hira ta/chi/tsu/te/to {'\u3060', "^a"}, {'\u3062', "i"}, {'\u3065', "u"}, {'\u3067', "^e"}, {'\u3069', "^o"}, // Hira da/di/du/de/do {'\u3063', "/"}, // Hira ltsu {'\u306A', "^a"}, {'\u306B', "i"}, {'\u306C', "u"}, {'\u306D', "^e"}, {'\u306E', "^o"}, // Hira na/ni/nu/ne/no {'\u306F', "a"}, {'\u3072', "i"}, {'\u3075', "u"}, {'\u3078', "e"}, {'\u307B', "o"}, // Hira ha/hi/fu/he/ho {'\u3070', "`a"}, {'\u3073', "`i"}, {'\u3076', "`u"}, {'\u3079', "`e"}, {'\u307C', "`o"}, // Hira ba/bi/bu/be/bo {'\u3071', "`a"}, {'\u3074', "`i"}, {'\u3077', "`u"}, {'\u307A', "`e"}, {'\u307D', "`o"}, // Hira pa/pi/pu/pe/po {'\u307E', "`a"}, {'\u307F', "`i"}, {'\u3080', "`u"}, {'\u3081', "`e"}, {'\u3082', "`o"}, // Hira ma/mi/mu/me/mo {'\u3083', "a"}, {'\u3085', "u"}, {'\u3087', "o"}, // Hira lya/lyu/lyo {'\u3084', "a"}, {'\u3086', "u"}, {'\u3088', "o"}, // Hira ya/yu/yo {'\u3089', "^a"}, {'\u308A', "i"}, {'\u308B', "u"}, {'\u308C', "^e"}, {'\u308D', "^o"}, // Hira ra/ri/ru/re/ro {'\u308E', "a"}, {'\u3090', "i"}, {'\u3092', "o"}, // Hira lwa/i/wo {'\u308F', "^a"}, {'\u3091', "e"}, {'\u3093', "n"}, // Hira wa/e/n {'\u30A1', "a"}, {'\u30A3', "i"}, {'\u30A5', "u"}, {'\u30A7', "e"}, {'\u30A9', "o"}, // Kana la/li/lu/le/lo {'\u30A2', "a"}, {'\u30A4', "i"}, {'\u30A6', "u"}, {'\u30A8', "e"}, {'\u30AA', "o"}, // Kana a/i/u/e/o {'\u30AB', "a"}, {'\u30AD', "i"}, {'\u30AF', "u"}, {'\u30B1', "e"}, {'\u30B3', "o"}, // Kana ka/ki/ku/ke/ko {'\u30AC', "a"}, {'\u30AE', "i"}, {'\u30B0', "u"}, {'\u30B2', "e"}, {'\u30B4', "o"}, // Kana ga/gi/gu/ge/go {'\u30B5', "^a"}, {'\u30B7', "i"}, {'\u30B9', "u"}, {'\u30BB', "^e"}, {'\u30BD', "^o"}, // Kana sa/si/su/se/so {'\u30B6', "^a"}, {'\u30B8', "i"}, {'\u30BA', "u"}, {'\u30BC', "^e"}, {'\u30BE', "^o"}, // Kana za/zi/zu/ze/zo {'\u30BF', "^a"}, {'\u30C1', "i"}, {'\u30C4', "u"}, {'\u30C6', "^e"}, {'\u30C8', "^o"}, // Kana ta/chi/tsu/te/to {'\u30C0', "^a"}, {'\u30C2', "i"}, {'\u30C5', "u"}, {'\u30C7', "^e"}, {'\u30C9', "^o"}, // Kana da/di/du/de/do {'\u30C3', "/"}, // Kana ltsu {'\u30CA', "^a"}, {'\u30CB', "i"}, {'\u30CC', "u"}, {'\u30CD', "^e"}, {'\u30CE', "^o"}, // Kana na/ni/nu/ne/no {'\u30CF', "a"}, {'\u30D2', "i"}, {'\u30D5', "u"}, {'\u30D8', "e"}, {'\u30DB', "o"}, // Kana ha/hi/fu/he/ho {'\u30D0', "`a"}, {'\u30D3', "`i"}, {'\u30D6', "`u"}, {'\u30D9', "`e"}, {'\u30DC', "`o"}, // Kana ba/bi/bu/be/bo {'\u30D1', "`a"}, {'\u30D4', "`i"}, {'\u30D7', "`u"}, {'\u30DA', "`e"}, {'\u30DD', "`o"}, // Kana pa/pi/pu/pe/po {'\u30DE', "`a"}, {'\u30DF', "`i"}, {'\u30E0', "`u"}, {'\u30E1', "`e"}, {'\u30E2', "`o"}, // Kana ma/mi/mu/me/mo {'\u30E3', "a"}, {'\u30E5', "u"}, {'\u30E7', "o"}, // Kana lya/lyu/lyo {'\u30E4', "a"}, {'\u30E6', "u"}, {'\u30E8', "o"}, // Kana ya/yu/yo {'\u30E9', "^a"}, {'\u30EA', "i"}, {'\u30EB', "u"}, {'\u30EC', "^e"}, {'\u30ED', "^o"}, // Kana ra/ri/ru/re/ro {'\u30EE', "a"}, {'\u30F0', "i"}, {'\u30F2', "o"}, // Kana lwa/i/wo {'\u30EF', "^a"}, {'\u30F1', "e"}, {'\u30F3', "n"}, // Kana wa/e/n {'\u30F4', "`u"}, {'\u30F5', "a"}, {'\u30F6', "e"}, // Kana vu/lka/lke }; struct MorphData { public char morphChar; public string morphName; public float morphLength; } class PlayingData { public AudioClip audioClip; public float playingLength; public float playingTime; public int morphPos = -1; public float morphTime; public List morphDataList; } AudioSource _audioSource; List _playingDataList = new List(); int _playingPos = 0; bool _isPlayingAudioClip; bool _isPlayingMorph; int _validateMorphBits; public override bool isProcessing { get { if( this.speechAudioClip != null || !string.IsNullOrEmpty( this.speechMorphText ) ) { return true; } if( _playingDataList != null && _playingPos < _playingDataList.Count ) { return true; } return false; } } public override bool isAnimating { get { if( base.isAnimating ) { return true; } if( this.speechAudioClip != null || !string.IsNullOrEmpty( this.speechMorphText ) ) { return true; } if( _playingDataList != null && _playingPos < _playingDataList.Count ) { return true; } return false; } } protected override void Start() { base.Start(); if( _model != null ) { _audioSource = _model.GetAudioSource(); } } protected override void Update() { _UpdateSpeech( Time.deltaTime ); base.Update(); } public override void ForceUpdate() { _UpdateSpeech( 0.0f ); base.ForceUpdate(); } public void ResetSpeech() { this.speechAudioClip = null; this.speechMorphText = ""; if( _playingDataList.Count > 0 ) { if( _isPlayingAudioClip ) { _isPlayingAudioClip = false; if( _audioSource != null ) { _audioSource.Stop(); _audioSource.clip = null; } } if( _isPlayingMorph ) { _isPlayingMorph = false; base.morphName = ""; base.morphWeight = 0.0f; } _playingDataList.Clear(); _playingPos = 0; ForceUpdate(); } } void _UpdateSpeech( float deltaTime ) { _UpdatePlayingSpeech(); if( _playingDataList == null || _playingDataList.Count == 0 ) { return; } if( _playingPos >= _playingDataList.Count ) { if( _isPlayingAudioClip ) { _isPlayingAudioClip = false; if( _audioSource != null ) { _audioSource.Stop(); _audioSource.clip = null; } } if( _isPlayingMorph ) { _isPlayingMorph = false; base.morphName = ""; base.morphWeight = 0.0f; } _playingDataList.Clear(); _playingPos = 0; return; } PlayingData playingData = _playingDataList[_playingPos]; while( playingData.playingTime >= playingData.playingLength ) { if( _isPlayingAudioClip ) { _isPlayingAudioClip = false; if( _audioSource != null ) { _audioSource.Stop(); _audioSource.clip = null; } } float paddingTime = playingData.playingTime - playingData.playingLength; if( ++_playingPos >= _playingDataList.Count ) { if( _isPlayingMorph ) { _isPlayingMorph = false; base.morphName = ""; base.morphWeight = 0.0f; } return; } playingData = _playingDataList[_playingPos]; playingData.playingTime = paddingTime; } if( playingData.morphPos < 0 ) { playingData.morphPos = 0; if( playingData.audioClip != null ) { _isPlayingAudioClip = true; if( _audioSource != null ) { _audioSource.clip = playingData.audioClip; _audioSource.Play(); } } if( playingData.morphDataList != null && playingData.morphPos < playingData.morphDataList.Count ) { _isPlayingMorph = true; _UpdateMorph( playingData.morphDataList[playingData.morphPos].morphName ); } else { if( _isPlayingMorph ) { _isPlayingMorph = false; base.morphName = ""; base.morphWeight = 0.0f; } } } bool overrayPlayingTime = false; if( _isPlayingAudioClip ) { if( _audioSource != null ) { if( _audioSource.isPlaying ) { playingData.playingTime = _audioSource.time; overrayPlayingTime = true; } else { if( playingData.playingTime < playingData.playingLength ) { playingData.playingTime = playingData.playingLength; overrayPlayingTime = true; } } } } if( playingData.morphTime < playingData.playingTime ) { if( playingData.morphDataList != null ) { float addingTime = playingData.playingTime - playingData.morphTime; int beforePos = playingData.morphPos; for( ; playingData.morphPos < playingData.morphDataList.Count; ++playingData.morphPos ) { float morphLength = playingData.morphDataList[playingData.morphPos].morphLength; if( morphLength >= addingTime ) { break; } playingData.morphTime += morphLength; addingTime -= morphLength; } if( beforePos != playingData.morphPos && playingData.morphPos < playingData.morphDataList.Count ) { _UpdateMorph( playingData.morphDataList[playingData.morphPos].morphName ); } } } if( !overrayPlayingTime ) { playingData.playingTime += deltaTime; } } void _UpdateMorph( string morphName ) { if( _validateMorphBits == 0 ) { // Collect enable morph. MMD4MecanimModel model = GetComponent(); if( model != null && hiraMorphNameList != null ) { for( int i = 0; i < hiraMorphNameList.Count; ++i ) { if( model.GetMorph( hiraMorphNameList[i] ) != null ) { _validateMorphBits |= 1 << i; } } } if( _validateMorphBits == 0 ) { _validateMorphBits = 0x1f; } } float morphWeight = 0.0f; if( !string.IsNullOrEmpty( morphName ) ) { // Emulation morph using 'a' morphWeight = 1.0f; if( hiraMorphNameList != null ) { if( morphName == hiraMorphNameList[1] && ( ( _validateMorphBits & 0x2 ) == 0 ) ) { morphName = hiraMorphNameList[0]; morphWeight = 0.5f; } else if( morphName == hiraMorphNameList[2] && ( ( _validateMorphBits & 0x4 ) == 0 ) ) { morphName = hiraMorphNameList[0]; morphWeight = 0.3f; } else if( morphName == hiraMorphNameList[3] && ( ( _validateMorphBits & 0x8 ) == 0 ) ) { morphName = hiraMorphNameList[0]; morphWeight = 0.5f; } else if( morphName == hiraMorphNameList[4] && ( ( _validateMorphBits & 0x10 ) == 0 ) ) { morphName = hiraMorphNameList[0]; morphWeight = 0.8f; } } } base.morphName = morphName; base.morphWeight = morphWeight; } void _UpdatePlayingSpeech() { if( this.speechAudioClip != null || !string.IsNullOrEmpty( this.speechMorphText ) ) { PlayingData playingData = _ParsePlayingData( this.speechAudioClip, this.speechMorphText ); this.speechAudioClip = null; this.speechMorphText = ""; _playingDataList.Clear(); _playingPos = 0; if( _isPlayingAudioClip ) { _isPlayingAudioClip = false; if( _audioSource != null ) { _audioSource.Stop(); _audioSource.clip = null; } } if( playingData != null ) { _playingDataList.Add( playingData ); } } } PlayingData _ParsePlayingData( AudioClip audioClip, string morphText ) { PlayingData playingData = new PlayingData(); playingData.audioClip = audioClip; playingData.playingLength = 0.0f; playingData.playingTime = 0.0f; playingData.morphPos = -1; playingData.morphTime = 0.0f; if( !string.IsNullOrEmpty( morphText ) ) { playingData.morphDataList = _ParseMorphText( morphText ); } else { playingData.morphDataList = _ParseMorphText( System.IO.Path.GetFileNameWithoutExtension( audioClip.name ) ); } if( playingData.morphDataList != null ) { float morphTotalLength = 0.0f; int morphZeroCount = 0; for( int i = 0; i < playingData.morphDataList.Count; ++i ) { char ch = playingData.morphDataList[i].morphChar; if( playingData.morphDataList[i].morphLength == 0.0f ) { if( ch != '^' && ch != '`' ) { ++morphZeroCount; } } else { morphTotalLength += playingData.morphDataList[i].morphLength; } } float elementLength = GetElementLength(); if( audioClip != null ) { playingData.playingLength = audioClip.length; if( playingData.playingLength <= morphTotalLength ) { playingData.playingLength = morphTotalLength; elementLength = 0.0f; } else { if( morphZeroCount > 0 ) { elementLength = (playingData.playingLength - morphTotalLength) / (float)morphZeroCount; } } } else { playingData.playingLength = morphTotalLength + elementLength * (float)morphZeroCount; } if( elementLength > 0.0f ) { // Allocate each morphLength. for( int i = 0; i < playingData.morphDataList.Count; ++i ) { char ch = playingData.morphDataList[i].morphChar; if( playingData.morphDataList[i].morphLength == 0.0f ) { if( ch != '^' && ch != '`' ) { MorphData morphData = playingData.morphDataList[i]; morphData.morphLength = elementLength; playingData.morphDataList[i] = morphData; } } } // Fix for each consonantLength. float consonantLength = GetConsonantLength(); for( int i = 0; i < playingData.morphDataList.Count; ++i ) { char ch = playingData.morphDataList[i].morphChar; if( playingData.morphDataList[i].morphLength == 0.0f ) { if( ch != '^' && ch != '`' ) { // Nothing. } else { int beginIndex = i; for( ++i; i < playingData.morphDataList.Count; ++i ) { ch = playingData.morphDataList[i].morphChar; if( ch != '^' && ch != '`' ) { float morphLength = playingData.morphDataList[i].morphLength; float tempLength = 0.0f; if( consonantLength * 2.0f <= morphLength ) { MorphData morphData = playingData.morphDataList[i]; morphData.morphLength = morphLength - consonantLength; playingData.morphDataList[i] = morphData; tempLength = consonantLength; } else { MorphData morphData = playingData.morphDataList[i]; morphData.morphLength = morphLength * 0.5f; playingData.morphDataList[i] = morphData; tempLength = morphData.morphLength; } { MorphData morphData = playingData.morphDataList[beginIndex]; morphData.morphLength = tempLength; playingData.morphDataList[beginIndex] = morphData; } break; } else { if( playingData.morphDataList[i].morphLength == 0.0f ) { // Nothing. } else { break; // Skip processing.( Exception case, no effects. ) } } } } } } } } else { if( audioClip != null ) { playingData.playingLength = audioClip.length; } } #if false if( playingData.morphDataList != null ) { for( int i = 0; i < playingData.morphDataList.Count; ++i ) { Debug.Log( "" + playingData.morphDataList[i].morphName + ":" + playingData.morphDataList[i].morphChar + ":" + playingData.morphDataList[i].morphLength ); } } #endif return playingData; } List _ParseMorphText( string morphText ) { if( string.IsNullOrEmpty( morphText ) ) { return null; } //Debug.Log( "_ParseMorphText:" + morphText ); // for Debug. System.Text.StringBuilder alphabets = new System.Text.StringBuilder(); List morphDataList = new List(); bool isBeforeAlphabet = false; bool isBeforeMorph = false; int textPos = 0; for(;;) { if( textPos >= morphText.Length ) { break; } string japanesePronun = null; char ch = morphText[textPos]; if( ch == '[' ) { int beginPos = (++textPos); for( ; textPos < morphText.Length; ++textPos ) { if( morphText[textPos] == ']' ) { int milliSeconds = MMD4MecanimCommon.ToInt( morphText, beginPos, textPos - beginPos ); if( isBeforeMorph && morphDataList.Count > 0 ) { MorphData morphData = morphDataList[morphDataList.Count - 1]; morphData.morphLength = (float)milliSeconds * 0.001f; morphDataList[morphDataList.Count - 1] = morphData; } else { MorphData morphData = new MorphData(); morphData.morphChar = ' '; morphData.morphName = ""; morphData.morphLength = (float)milliSeconds * 0.001f; morphDataList.Add( morphData ); } ++textPos; break; } } isBeforeMorph = false; } else if( _IsEscape( ch ) ) { // ^ ... alveolar // ` ... bilabial // / ... staccato // - ... - // ~ ... - MorphData morphData = new MorphData(); morphData.morphChar = ch; morphData.morphName = ""; switch( ch ) { case '^': morphData.morphName = hiraMorphNameList[2]; // u break; case '`': morphData.morphName = ""; // n break; case '/': case '-': case '~': if( morphDataList.Count > 0 ) { morphData.morphName = morphDataList[morphDataList.Count - 1].morphName; } break; } isBeforeMorph = true; morphDataList.Add( morphData ); ++textPos; } else if( MMD4MecanimCommon.IsAlphabet( ch ) ) { // for English. string tempString = null; bool processedAnything = false; ch = MMD4MecanimCommon.ToHalfLower( ch ); if( textPos + 1 < morphText.Length && MMD4MecanimCommon.IsAlphabet( morphText[textPos + 1] ) ) { char ch2 = MMD4MecanimCommon.ToHalfLower( morphText[textPos + 1] ); if( textPos + 2 < morphText.Length && MMD4MecanimCommon.IsAlphabet( morphText[textPos + 2] ) ) { char ch3 = MMD4MecanimCommon.ToHalfLower( morphText[textPos + 2] ); alphabets.Remove( 0, alphabets.Length ); alphabets.Append( ch ); alphabets.Append( ch2 ); alphabets.Append( ch3 ); if( englishPhraseDictionary.TryGetValue( alphabets.ToString(), out tempString ) ) { _AddMorphData( morphDataList, tempString ); processedAnything = true; textPos += 3; } } if( !processedAnything ) { alphabets.Remove( 0, alphabets.Length ); alphabets.Append( ch ); alphabets.Append( ch2 ); if( englishPhraseDictionary.TryGetValue( alphabets.ToString(), out tempString ) ) { _AddMorphData( morphDataList, tempString ); processedAnything = true; textPos += 2; } } } if( !processedAnything ) { if( !isBeforeAlphabet && ( morphText[textPos] == 'I' || morphText[textPos] == '\uFF29' ) ) { _AddMorphData( morphDataList, "ai" ); } else { if( textPos > 0 && MMD4MecanimCommon.ToHalfLower( morphText[textPos - 1] ) == 'e' && ch == 'e' ) { // lee, bee, ... _AddMorphData( morphDataList, "-" ); } else { if( englishPronunDictionary.TryGetValue( ch, out tempString ) ) { _AddMorphData( morphDataList, tempString ); } else { _AddMorphData( morphDataList, " " ); } } } processedAnything = true; ++textPos; } isBeforeAlphabet = true; isBeforeMorph = true; } else if( japanesePronunDictionary.TryGetValue( ch, out japanesePronun ) ) { string postPhaseText = null; if( textPos + 1 < morphText.Length && japanesePostPhraseDictionary.TryGetValue( morphText[textPos + 1], out postPhaseText ) ) { string phaseText = null; if( japanesePhraseDictionary.TryGetValue( morphText.Substring( textPos, 2 ), out phaseText ) ) { _AddMorphData( morphDataList, phaseText ); } else { _AddMorphData( morphDataList, japanesePronun ); _AddMorphData( morphDataList, postPhaseText ); } textPos += 2; } else { _AddMorphData( morphDataList, japanesePronun ); ++textPos; } isBeforeAlphabet = false; isBeforeMorph = true; } else { if( textPos + 1 < morphText.Length ) { // Simulate VOICEROID string tempString = null; if( punctuatDictionary.TryGetValue( ch, out tempString ) ) { _AddMorphData( morphDataList, " " ); // Simulate VOICEROID } else { // Nothing } } isBeforeAlphabet = false; isBeforeMorph = false; ++textPos; } } return morphDataList; } void _AddMorphData( List morphDataList, string morphScript ) { if( morphDataList != null && morphScript != null ) { MorphData morphData = new MorphData(); for( int i = 0; i < morphScript.Length; ++i ) { //Debug.Log( "Char:" + morphScript[i] ); morphData.morphChar = morphScript[i]; morphData.morphName = ""; switch( morphData.morphChar ) { case 'a': morphData.morphName = hiraMorphNameList[0]; break; case 'i': morphData.morphName = hiraMorphNameList[1]; break; case 'u': morphData.morphName = hiraMorphNameList[2]; break; case 'e': morphData.morphName = hiraMorphNameList[3]; break; case 'o': morphData.morphName = hiraMorphNameList[4]; break; case 'n': morphData.morphName = hiraMorphNameList[2]; break; // u case '^': morphData.morphName = hiraMorphNameList[2]; break; // u case '`': morphData.morphName = ""; break; // n case '/': case '-': case '~': if( morphDataList.Count > 0 ) { morphData.morphName = morphDataList[morphDataList.Count - 1].morphName; } break; } morphDataList.Add( morphData ); } } } }