Skip to content

Instantly share code, notes, and snippets.

@TakaakiIchijo
Created December 9, 2023 09:17
Show Gist options
  • Save TakaakiIchijo/ff37f586a217430b35688e90d6c4d14c to your computer and use it in GitHub Desktop.
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
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