Created
December 9, 2023 09:17
-
-
Save TakaakiIchijo/ff37f586a217430b35688e90d6c4d14c to your computer and use it in GitHub Desktop.
Convert text to voice on timeline text track subtitles to wav files with A.I.VOICE for Games
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Globalization; | |
using System.IO; | |
using System.Linq; | |
using UnityEngine; | |
using CsvHelper; | |
using UnityEditor; | |
using UnityEditor.Timeline; | |
using UnityEngine.Timeline; | |
public class ConvertTimelineTextToVoiceWav : EditorWindow | |
{ | |
const string csvExportFilePath = "Assets/CSVTest.csv"; | |
const string voiceExportPath = "Assets/Voices/"; | |
const string timelineFilePath = "Assets/Timeline/TestTimeLine.playable"; | |
const string TEXT_SWITCHER_TRACK_NAME = "Text Switcher Track"; | |
const string AUDIO_TRACK_NAME = "Audio Track"; | |
[MenuItem("Window/Custom/ConvertTimelineTextToVoiceWav")] | |
public static void ShowWindow() | |
{ | |
// EditorWindowを作成 | |
// 型引数にはこのクラスを入れる | |
ConvertTimelineTextToVoiceWav window = GetWindow<ConvertTimelineTextToVoiceWav>(); | |
window.titleContent = new GUIContent("CsvCreator"); | |
} | |
private void OnGUI() | |
{ | |
if (GUILayout.Button("Convert",GUILayout.Width( 200 ),GUILayout.Height( 100 ))) | |
{ | |
Extract(); | |
} | |
if (GUILayout.Button("Attach",GUILayout.Width( 200 ),GUILayout.Height( 100 ))) | |
{ | |
Attach(); | |
} | |
} | |
void Extract() | |
{ | |
TimelineAsset timelineAsset = AssetDatabase.LoadAssetAtPath<TimelineAsset>(timelineFilePath); | |
var textDataList = new List<(string clipName, string text)>(); | |
if( timelineAsset != null ) | |
{ | |
IEnumerable<TrackAsset> tracks= timelineAsset.GetOutputTracks(); | |
var textSwitcherTrack = tracks.FirstOrDefault(track => track.name == TEXT_SWITCHER_TRACK_NAME); | |
if (textSwitcherTrack != null) | |
{ | |
foreach (TimelineClip timelineClip in textSwitcherTrack.GetClips()) | |
{ | |
TextSwitcherClip tsc = (TextSwitcherClip)timelineClip.asset; | |
textDataList.Add((timelineClip.displayName, tsc.template.text)); | |
Debug.Log(timelineClip.displayName + ", " + tsc.template.text); | |
} | |
} | |
} | |
var records = new List<AIVOICEData>(); | |
foreach (var textData in textDataList) | |
{ | |
var record = new AIVOICEData() | |
{ | |
ファイルパス = voiceExportPath + textData.clipName + ".aivoice", | |
セリフ = textData.text, | |
話者名 = "ユニティちゃん", | |
音量 = 1.0f, 話速 = 1.0f, 高さ = 1.0f, 抑揚 = 0.5f, スタイル1 = 0.5f, スタイル2 = 0f, スタイル3 = 0f | |
}; | |
records.Add(record); | |
} | |
using (var writer = new StreamWriter(csvExportFilePath)) | |
{ | |
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) | |
{ | |
csv.WriteRecords(records); | |
} | |
} | |
AssetDatabase.SaveAssets(); | |
AssetDatabase.Refresh(); | |
} | |
private void Attach() | |
{ | |
TimelineAsset timelineAsset = AssetDatabase.LoadAssetAtPath<TimelineAsset>(timelineFilePath); | |
//テキストの名前とタイミング情報を取得 | |
var clipNameAndStartTime = new List<(string clipName, double startTime)>(); | |
if( timelineAsset != null ) | |
{ | |
IEnumerable<TrackAsset> tracks= timelineAsset.GetOutputTracks(); | |
var textSwitcherTrack = tracks.FirstOrDefault(track => track.name == TEXT_SWITCHER_TRACK_NAME); | |
if (textSwitcherTrack != null) | |
{ | |
foreach (TimelineClip timelineClip in textSwitcherTrack.GetClips()) | |
{ | |
clipNameAndStartTime.Add((timelineClip.displayName, timelineClip.start)); | |
} | |
} | |
var audioTrack = tracks.FirstOrDefault(track => track.name == AUDIO_TRACK_NAME); | |
DeleteAllClips(timelineAsset,audioTrack); | |
foreach (var clipNameAndTime in clipNameAndStartTime) | |
{ | |
var clip = audioTrack.CreateDefaultClip(); | |
var audioPlayableAsset = clip.asset as AudioPlayableAsset; | |
var audioClip = AssetDatabase.LoadAssetAtPath<AudioClip>(voiceExportPath+ clipNameAndTime.clipName + ".wav"); | |
audioPlayableAsset.clip = audioClip; | |
clip.start = clipNameAndTime.startTime; | |
clip.duration = audioClip.length; | |
clip.asset = audioPlayableAsset; | |
} | |
} | |
TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved); | |
AssetDatabase.SaveAssets(); | |
AssetDatabase.Refresh(); | |
} | |
private void DeleteAllClips(TimelineAsset asset, TrackAsset track) | |
{ | |
var clips = track.GetClips().ToArray(); | |
foreach (var c in clips) | |
{ | |
asset.DeleteClip(c); | |
} | |
} | |
} | |
public class AIVOICEData | |
{ | |
public string ファイルパス { get; set; } | |
public string セリフ { get; set; } | |
public string 話者名 { get; set; } | |
public float 音量 { get; set; } | |
public float 話速 { get; set; } | |
public float 高さ { get; set; } | |
public float 抑揚 { get; set; } | |
public float スタイル1 { get; set; } | |
public float スタイル2 { get; set; } | |
public float スタイル3 { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment