-
Notifications
You must be signed in to change notification settings - Fork 6
/
DCS-SimpleTextToSpeech.lua
215 lines (156 loc) · 7.16 KB
/
DCS-SimpleTextToSpeech.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
--[[
DCS-SimpleTextToSpeech
Version 0.4
Compatible with SRS version 1.9.6.0 +
DCS Modification Required:
You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitisation.
To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)"
Do this without DCS running to allow mission scripts to use os functions.
*You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE*
USAGE:
Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialise it
Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission.
Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts
Example calls:
STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2)
Arguments in order are:
- Message to say, make sure not to use a newline (\n) !
- Frequency in MHz
- Modulation - AM/FM
- Volume - 1.0 max, 0.5 half
- Name of the transmitter - ATC, RockFM etc
- Coalition - 0 spectator, 1 red 2 blue
- OPTIONAL - Vec3 Point i.e Unit.getByName("A UNIT"):getPoint() - needs Vec3 for Height! OR null if not needed
- OPTIONAL - Speed -10 to +10
- OPTIONAL - Gender male, female or neuter
- OPTIONAL - Culture - en-US, en-GB etc
- OPTIONAL - Voice - a specfic voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line
- OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly
This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only
STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB")
This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on
the position of the Unit called "A UNIT"
STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB")
Arguments in order are:
- FULL path to the MP3 OR OGG to play
- Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations
- Modulation - AM/FM - to use multiple
- Volume - 1.0 max, 0.5 half
- Name of the transmitter - ATC, RockFM etc
- Coalition - 0 spectator, 1 red 2 blue
This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only
STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0)
]]
STTS = {}
-- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
STTS.DIRECTORY = "C:\\Users\\Ciaran\\Dropbox\\Dev\\DCS\\DCS-SRS\\install-build"
STTS.SRS_PORT = 5002 -- LOCAL SRS PORT - DEFAULT IS 5002
STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json"
-- DONT CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING
STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe"
local random = math.random
function STTS.uuid()
local template ='yxxx-xxxxxxxxxxxx'
return string.gsub(template, '[xy]', function (c)
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
return string.format('%x', v)
end)
end
function STTS.round(x, n)
n = math.pow(10, n or 0)
x = x * n
if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end
return x / n
end
function STTS.getSpeechTime(length,speed,isGoogle)
-- Function returns estimated speech time in seconds
-- Assumptions for time calc: 100 Words per min, avarage of 5 letters for english word
-- so 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
-- so lengh of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec
-- map function: (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
local maxRateRatio = 3
speed = speed or 1.0
isGoogle = isGoogle or false
local speedFactor = 1.0
if isGoogle then
speedFactor = speed
else
if speed ~= 0 then
speedFactor = math.abs(speed) * (maxRateRatio - 1) / 10 + 1
end
if speed < 0 then
speedFactor = 1/speedFactor
end
end
local wpm = math.ceil(100 * speedFactor)
local cps = math.floor((wpm * 5)/60)
if type(length) == "string" then
length = string.len(length)
end
return math.ceil(length/cps)
end
function STTS.TextToSpeech(message,freqs,modulations, volume,name, coalition,point, speed,gender,culture,voice, googleTTS )
if os == nil or io == nil then
env.info("[DCS-STTS] LUA modules os or io are sanitized. skipping. ")
return
end
speed = speed or 1
gender = gender or "female"
culture = culture or ""
voice = voice or ""
message = message:gsub("\"","\\\"")
local cmd = string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs, modulations, coalition,STTS.SRS_PORT, name )
if voice ~= "" then
cmd = cmd .. string.format(" -V \"%s\"",voice)
else
if culture ~= "" then
cmd = cmd .. string.format(" -l %s",culture)
end
if gender ~= "" then
cmd = cmd .. string.format(" -g %s",gender)
end
end
if googleTTS == true then
cmd = cmd .. string.format(" -G \"%s\"",STTS.GOOGLE_CREDENTIALS)
end
if speed ~= 1 then
cmd = cmd .. string.format(" -s %s",speed)
end
if volume ~= 1.0 then
cmd = cmd .. string.format(" -v %s",volume)
end
if point and type(point) == "table" and point.x then
local lat, lon, alt = coord.LOtoLL(point)
lat = STTS.round(lat,4)
lon = STTS.round(lon,4)
alt = math.floor(alt)
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt)
end
cmd = cmd ..string.format(" -t \"%s\"",message)
if string.len(cmd) > 255 then
local filename = os.getenv('TMP') .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
local script = io.open(filename,"w+")
script:write(cmd .. " && exit" )
script:close()
cmd = string.format("\"%s\"",filename)
timer.scheduleFunction(os.remove, filename, timer.getTime() + 1)
end
if string.len(cmd) > 255 then
env.info("[DCS-STTS] - cmd string too long")
env.info("[DCS-STTS] TextToSpeech Command :\n" .. cmd.."\n")
end
os.execute(cmd)
return STTS.getSpeechTime(message,speed,googleTTS)
end
function STTS.PlayMP3(pathToMP3,freqs,modulations, volume,name, coalition,point )
local cmd = string.format("start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h", STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs, modulations, coalition,STTS.SRS_PORT, name, volume )
if point and type(point) == "table" and point.x then
local lat, lon, alt = coord.LOtoLL(point)
lat = STTS.round(lat,4)
lon = STTS.round(lon,4)
alt = math.floor(alt)
cmd = cmd .. string.format(" -L %s -O %s -A %s",lat,lon,alt)
end
env.info("[DCS-STTS] MP3/OGG Command :\n" .. cmd.."\n")
os.execute(cmd)
end