Skip to content

Commit a0b2a0b

Browse files
committed
add openai text to speech tool
1 parent 1592e87 commit a0b2a0b

17 files changed

Lines changed: 2784 additions & 0 deletions

Assets/Scripts/Tools.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/Tools/OpenAITTS.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/Tools/OpenAITTS/Editor.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using UnityEditor;
2+
using UnityEngine;
3+
using UnityEngine.UIElements;
4+
using System.Text.RegularExpressions;
5+
using UnityEngine.Networking;
6+
using Codice.Utils;
7+
using System;
8+
using System.IO;
9+
10+
namespace Veridium.Tools
11+
{
12+
public class OpenAITTS : EditorWindow
13+
{
14+
string text = "";
15+
string prefix = "in";
16+
string suffix = "_DE";
17+
string secretKey = "";
18+
19+
[MenuItem("Tools/Text to Speech (OpenAI)")]
20+
public static void ShowExample()
21+
{
22+
OpenAITTS wnd = GetWindow<OpenAITTS>();
23+
wnd.titleContent = new GUIContent("Text to Speech (OpenAI)");
24+
}
25+
26+
public void CreateGUI()
27+
{
28+
// Each editor window contains a root VisualElement object
29+
VisualElement root = rootVisualElement;
30+
31+
32+
// try to load secret key from file
33+
string secretKeyPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + Path.DirectorySeparatorChar + ".openaikey";
34+
if (System.IO.File.Exists(secretKeyPath))
35+
{
36+
secretKey = System.IO.File.ReadAllText(secretKeyPath).Trim();
37+
Debug.Log("Secret key found: " + secretKey);
38+
} else
39+
{
40+
Label label = new Label("Secret key not found. Please create a file at " + secretKeyPath + " with your OpenAI secret key.");
41+
label.style.whiteSpace = WhiteSpace.Normal;
42+
root.Add(label);
43+
44+
return;
45+
}
46+
47+
// scrool view
48+
ScrollView scrollView = new ScrollView();
49+
root.Add(scrollView);
50+
51+
TextField textField = new TextField();
52+
textField.value = text;
53+
textField.RegisterValueChangedCallback(evt => text = evt.newValue);
54+
textField.label = "";
55+
textField.multiline = true;
56+
textField.style.whiteSpace = WhiteSpace.Normal;
57+
scrollView.Add(textField);
58+
59+
TextField prefixField = new TextField("Prefix");
60+
prefixField.value = prefix;
61+
prefixField.RegisterValueChangedCallback(evt => prefix = evt.newValue);
62+
root.Add(prefixField);
63+
64+
TextField suffixField = new TextField("Suffix");
65+
suffixField.value = suffix;
66+
suffixField.RegisterValueChangedCallback(evt => suffix = evt.newValue);
67+
root.Add(suffixField);
68+
69+
Button button = new Button();
70+
button.name = "generate";
71+
button.text = "Generate speech";
72+
button.clickable.clicked += GenerateSpeech;
73+
root.Add(button);
74+
}
75+
76+
public void GenerateSpeech()
77+
{
78+
string cleanText = Regex.Replace(text, @"<[^>]*>", "").Replace("\n", " ");
79+
80+
// split text by [CUT]
81+
string[] splitText = cleanText.Replace("[CUT]", "\n").Split('\n');
82+
83+
for (int i = 0; i < splitText.Length; i++)
84+
{
85+
string s = splitText[i];
86+
87+
GenerateClip(s, Application.dataPath + "/TTSOutput/" + prefix + (i+1) + suffix + ".mp3");
88+
}
89+
}
90+
91+
public void GenerateClip(string text, string outputFilePath) {
92+
text = HttpUtility.JavaScriptStringEncode(text);
93+
string requestBody = "{\n \"model\": \"tts-1\",\n \"input\": \"" + text + "\",\n \"voice\": \"alloy\"\n}";
94+
95+
// send request to OpenAI
96+
var request = UnityWebRequest.Put("https://api.openai.com/v1/audio/speech", requestBody);
97+
98+
request.SetRequestHeader("Authorization", "Bearer "+ secretKey);
99+
request.SetRequestHeader("Content-Type", "application/json");
100+
101+
// create via PUT and change to POST later because UnityWebRequest.Post is broken
102+
request.method = "POST";
103+
104+
var res = request.SendWebRequest();
105+
106+
res.completed += (op) =>
107+
{
108+
if (request.result == UnityWebRequest.Result.Success)
109+
{
110+
byte[] audio = request.downloadHandler.data;
111+
File.WriteAllBytes(outputFilePath, audio);
112+
Debug.Log("Audio saved to " + outputFilePath);
113+
114+
AssetDatabase.Refresh();
115+
}
116+
else
117+
{
118+
Debug.Log(request.error);
119+
}
120+
};
121+
}
122+
}
123+
}

Assets/Scripts/Tools/OpenAITTS/Editor/OpenAITTS.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/TTSOutput.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<xs:schema xmlns:editor="UnityEditor.UIElements" xmlns:engine="UnityEngine.UIElements" xmlns="UnityEditor.PackageManager.UI" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+
<xs:import schemaLocation="UnityEngine.UIElements.xsd" namespace="UnityEngine.UIElements" />
4+
<xs:complexType name="TabButtonType">
5+
<xs:complexContent mixed="false">
6+
<xs:restriction base="engine:VisualElementType">
7+
<xs:sequence minOccurs="0" maxOccurs="unbounded">
8+
<xs:element ref="engine:VisualElement" />
9+
</xs:sequence>
10+
<xs:attribute default="" name="name" type="xs:string" use="optional" />
11+
<xs:attribute default="" name="view-data-key" type="xs:string" use="optional" />
12+
<xs:attribute default="Position" name="picking-mode" type="engine:VisualElement_picking-mode_Type" use="optional" />
13+
<xs:attribute default="" name="tooltip" type="xs:string" use="optional" />
14+
<xs:attribute default="None" name="usage-hints" type="engine:VisualElement_usage-hints_Type" use="optional" />
15+
<xs:attribute default="0" name="tabindex" type="xs:int" use="optional" />
16+
<xs:attribute default="false" name="focusable" type="xs:boolean" use="optional" />
17+
<xs:attribute default="" name="class" type="xs:string" use="optional" />
18+
<xs:attribute default="" name="content-container" type="xs:string" use="optional" />
19+
<xs:attribute default="" name="style" type="xs:string" use="optional" />
20+
<xs:attribute default="" name="text" type="xs:string" use="optional" />
21+
<xs:attribute default="" name="target" type="xs:string" use="optional" />
22+
<xs:anyAttribute processContents="lax" />
23+
</xs:restriction>
24+
</xs:complexContent>
25+
</xs:complexType>
26+
<xs:element name="TabButton" substitutionGroup="engine:VisualElement" type="TabButtonType" />
27+
<xs:complexType name="TabbedViewType">
28+
<xs:complexContent mixed="false">
29+
<xs:restriction base="engine:VisualElementType">
30+
<xs:sequence minOccurs="0" maxOccurs="unbounded">
31+
<xs:element ref="engine:VisualElement" />
32+
</xs:sequence>
33+
<xs:attribute default="" name="name" type="xs:string" use="optional" />
34+
<xs:attribute default="" name="view-data-key" type="xs:string" use="optional" />
35+
<xs:attribute default="Position" name="picking-mode" type="engine:VisualElement_picking-mode_Type" use="optional" />
36+
<xs:attribute default="" name="tooltip" type="xs:string" use="optional" />
37+
<xs:attribute default="None" name="usage-hints" type="engine:VisualElement_usage-hints_Type" use="optional" />
38+
<xs:attribute default="0" name="tabindex" type="xs:int" use="optional" />
39+
<xs:attribute default="false" name="focusable" type="xs:boolean" use="optional" />
40+
<xs:attribute default="" name="class" type="xs:string" use="optional" />
41+
<xs:attribute default="" name="content-container" type="xs:string" use="optional" />
42+
<xs:attribute default="" name="style" type="xs:string" use="optional" />
43+
<xs:anyAttribute processContents="lax" />
44+
</xs:restriction>
45+
</xs:complexContent>
46+
</xs:complexType>
47+
<xs:element name="TabbedView" substitutionGroup="engine:VisualElement" type="TabbedViewType" />
48+
</xs:schema>

UIElementsSchema/UIElements.xsd

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<xs:schema xmlns:editor="UnityEditor.UIElements" xmlns:engine="UnityEngine.UIElements" xmlns="UnityEditor.PackageManager.UI" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+
<xs:import schemaLocation="UnityEngine.UIElements.xsd" namespace="UnityEngine.UIElements" />
4+
<xs:import schemaLocation="UnityEditor.UIElements.xsd" namespace="UnityEditor.UIElements" />
5+
<xs:import schemaLocation="UnityEditor.UIElements.Debugger.xsd" namespace="UnityEditor.UIElements.Debugger" />
6+
<xs:import schemaLocation="UnityEditor.Rendering.LookDev.xsd" namespace="UnityEditor.Rendering.LookDev" />
7+
<xs:import schemaLocation="Unity.Cloud.Collaborate.Views.xsd" namespace="Unity.Cloud.Collaborate.Views" />
8+
<xs:import schemaLocation="Unity.Cloud.Collaborate.Components.xsd" namespace="Unity.Cloud.Collaborate.Components" />
9+
<xs:import schemaLocation="Unity.Cloud.Collaborate.Components.ChangeListEntries.xsd" namespace="Unity.Cloud.Collaborate.Components.ChangeListEntries" />
10+
<xs:import schemaLocation="UnityEditor.ShaderGraph.Drawing.xsd" namespace="UnityEditor.ShaderGraph.Drawing" />
11+
<xs:include schemaLocation="GlobalNamespace.xsd" />
12+
<xs:import schemaLocation="UnityEditor.PackageManager.UI.xsd" namespace="UnityEditor.PackageManager.UI" />
13+
</xs:schema>
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<xs:schema xmlns:editor="UnityEditor.UIElements" xmlns:engine="UnityEngine.UIElements" xmlns="UnityEditor.PackageManager.UI" elementFormDefault="qualified" targetNamespace="Unity.Cloud.Collaborate.Components.ChangeListEntries" xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+
<xs:import schemaLocation="UnityEngine.UIElements.xsd" namespace="UnityEngine.UIElements" />
4+
<xs:complexType name="BaseChangeListElementType">
5+
<xs:complexContent mixed="false">
6+
<xs:restriction base="engine:VisualElementType">
7+
<xs:sequence minOccurs="0" maxOccurs="unbounded">
8+
<xs:element ref="engine:VisualElement" />
9+
</xs:sequence>
10+
<xs:attribute default="" name="name" type="xs:string" use="optional" />
11+
<xs:attribute default="" name="view-data-key" type="xs:string" use="optional" />
12+
<xs:attribute default="Position" name="picking-mode" type="engine:VisualElement_picking-mode_Type" use="optional" />
13+
<xs:attribute default="" name="tooltip" type="xs:string" use="optional" />
14+
<xs:attribute default="None" name="usage-hints" type="engine:VisualElement_usage-hints_Type" use="optional" />
15+
<xs:attribute default="0" name="tabindex" type="xs:int" use="optional" />
16+
<xs:attribute default="false" name="focusable" type="xs:boolean" use="optional" />
17+
<xs:attribute default="" name="class" type="xs:string" use="optional" />
18+
<xs:attribute default="" name="content-container" type="xs:string" use="optional" />
19+
<xs:attribute default="" name="style" type="xs:string" use="optional" />
20+
<xs:anyAttribute processContents="lax" />
21+
</xs:restriction>
22+
</xs:complexContent>
23+
</xs:complexType>
24+
<xs:element name="BaseChangeListElement" substitutionGroup="engine:VisualElement" xmlns:q1="Unity.Cloud.Collaborate.Components.ChangeListEntries" type="q1:BaseChangeListElementType" />
25+
<xs:complexType name="ChangeListElementType">
26+
<xs:complexContent mixed="false">
27+
<xs:restriction base="engine:VisualElementType">
28+
<xs:sequence minOccurs="0" maxOccurs="unbounded">
29+
<xs:element ref="engine:VisualElement" />
30+
</xs:sequence>
31+
<xs:attribute default="" name="name" type="xs:string" use="optional" />
32+
<xs:attribute default="" name="view-data-key" type="xs:string" use="optional" />
33+
<xs:attribute default="Position" name="picking-mode" type="engine:VisualElement_picking-mode_Type" use="optional" />
34+
<xs:attribute default="" name="tooltip" type="xs:string" use="optional" />
35+
<xs:attribute default="None" name="usage-hints" type="engine:VisualElement_usage-hints_Type" use="optional" />
36+
<xs:attribute default="0" name="tabindex" type="xs:int" use="optional" />
37+
<xs:attribute default="false" name="focusable" type="xs:boolean" use="optional" />
38+
<xs:attribute default="" name="class" type="xs:string" use="optional" />
39+
<xs:attribute default="" name="content-container" type="xs:string" use="optional" />
40+
<xs:attribute default="" name="style" type="xs:string" use="optional" />
41+
<xs:anyAttribute processContents="lax" />
42+
</xs:restriction>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
<xs:element name="ChangeListElement" substitutionGroup="engine:VisualElement" xmlns:q2="Unity.Cloud.Collaborate.Components.ChangeListEntries" type="q2:ChangeListElementType" />
46+
<xs:complexType name="ConflictedChangeListElementType">
47+
<xs:complexContent mixed="false">
48+
<xs:restriction base="engine:VisualElementType">
49+
<xs:sequence minOccurs="0" maxOccurs="unbounded">
50+
<xs:element ref="engine:VisualElement" />
51+
</xs:sequence>
52+
<xs:attribute default="" name="name" type="xs:string" use="optional" />
53+
<xs:attribute default="" name="view-data-key" type="xs:string" use="optional" />
54+
<xs:attribute default="Position" name="picking-mode" type="engine:VisualElement_picking-mode_Type" use="optional" />
55+
<xs:attribute default="" name="tooltip" type="xs:string" use="optional" />
56+
<xs:attribute default="None" name="usage-hints" type="engine:VisualElement_usage-hints_Type" use="optional" />
57+
<xs:attribute default="0" name="tabindex" type="xs:int" use="optional" />
58+
<xs:attribute default="false" name="focusable" type="xs:boolean" use="optional" />
59+
<xs:attribute default="" name="class" type="xs:string" use="optional" />
60+
<xs:attribute default="" name="content-container" type="xs:string" use="optional" />
61+
<xs:attribute default="" name="style" type="xs:string" use="optional" />
62+
<xs:anyAttribute processContents="lax" />
63+
</xs:restriction>
64+
</xs:complexContent>
65+
</xs:complexType>
66+
<xs:element name="ConflictedChangeListElement" substitutionGroup="engine:VisualElement" xmlns:q3="Unity.Cloud.Collaborate.Components.ChangeListEntries" type="q3:ConflictedChangeListElementType" />
67+
<xs:complexType name="HistoryChangeListElementType">
68+
<xs:complexContent mixed="false">
69+
<xs:restriction base="engine:VisualElementType">
70+
<xs:sequence minOccurs="0" maxOccurs="unbounded">
71+
<xs:element ref="engine:VisualElement" />
72+
</xs:sequence>
73+
<xs:attribute default="" name="name" type="xs:string" use="optional" />
74+
<xs:attribute default="" name="view-data-key" type="xs:string" use="optional" />
75+
<xs:attribute default="Position" name="picking-mode" type="engine:VisualElement_picking-mode_Type" use="optional" />
76+
<xs:attribute default="" name="tooltip" type="xs:string" use="optional" />
77+
<xs:attribute default="None" name="usage-hints" type="engine:VisualElement_usage-hints_Type" use="optional" />
78+
<xs:attribute default="0" name="tabindex" type="xs:int" use="optional" />
79+
<xs:attribute default="false" name="focusable" type="xs:boolean" use="optional" />
80+
<xs:attribute default="" name="class" type="xs:string" use="optional" />
81+
<xs:attribute default="" name="content-container" type="xs:string" use="optional" />
82+
<xs:attribute default="" name="style" type="xs:string" use="optional" />
83+
<xs:anyAttribute processContents="lax" />
84+
</xs:restriction>
85+
</xs:complexContent>
86+
</xs:complexType>
87+
<xs:element name="HistoryChangeListElement" substitutionGroup="engine:VisualElement" xmlns:q4="Unity.Cloud.Collaborate.Components.ChangeListEntries" type="q4:HistoryChangeListElementType" />
88+
<xs:complexType name="ToggleableChangeListElementType">
89+
<xs:complexContent mixed="false">
90+
<xs:restriction base="engine:VisualElementType">
91+
<xs:sequence minOccurs="0" maxOccurs="unbounded">
92+
<xs:element ref="engine:VisualElement" />
93+
</xs:sequence>
94+
<xs:attribute default="" name="name" type="xs:string" use="optional" />
95+
<xs:attribute default="" name="view-data-key" type="xs:string" use="optional" />
96+
<xs:attribute default="Position" name="picking-mode" type="engine:VisualElement_picking-mode_Type" use="optional" />
97+
<xs:attribute default="" name="tooltip" type="xs:string" use="optional" />
98+
<xs:attribute default="None" name="usage-hints" type="engine:VisualElement_usage-hints_Type" use="optional" />
99+
<xs:attribute default="0" name="tabindex" type="xs:int" use="optional" />
100+
<xs:attribute default="false" name="focusable" type="xs:boolean" use="optional" />
101+
<xs:attribute default="" name="class" type="xs:string" use="optional" />
102+
<xs:attribute default="" name="content-container" type="xs:string" use="optional" />
103+
<xs:attribute default="" name="style" type="xs:string" use="optional" />
104+
<xs:anyAttribute processContents="lax" />
105+
</xs:restriction>
106+
</xs:complexContent>
107+
</xs:complexType>
108+
<xs:element name="ToggleableChangeListElement" substitutionGroup="engine:VisualElement" xmlns:q5="Unity.Cloud.Collaborate.Components.ChangeListEntries" type="q5:ToggleableChangeListElementType" />
109+
</xs:schema>

0 commit comments

Comments
 (0)