From 52c2401d93518272f1780754aa808d85fdc4043c Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 22 Nov 2023 22:53:54 +0100 Subject: [PATCH 01/10] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 70 +++++++++++++++------------ 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 537825b1b..196729143 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -5,7 +5,7 @@ -- **Main Features:** -- -- * Play sound files via SRS --- * Play text-to-speach via SRS +-- * Play text-to-speech via SRS -- -- === -- @@ -65,6 +65,16 @@ -- -- This script needs SRS version >= 1.9.6. -- +-- ## Knwon Issues +-- +-- ### Pop-up Window +-- +-- The text-to-speech conversion of SRS is done via an external exe file. When this file is called, a windows `cmd` window is briefly opended. That puts DCS out of focus, which is annoying, +-- expecially in VR but unavoidable (if you have a solution, please feel free to share!). +-- +-- NOTE that this is not an issue if the mission is running on a server. +-- Also NOTE that using DCS-gRPC as backend will avoid the pop-up window. +-- -- # Play Sound Files -- -- local soundfile=SOUNDFILE:New("My Soundfile.ogg", "D:\\Sounds For DCS") @@ -188,7 +198,7 @@ MSRS = { gender = "female", culture = nil, voice = nil, - volume = 1, + volume = 1, speed = 1, coordinate = nil, Label = "ROBOT", @@ -201,7 +211,7 @@ MSRS = { --- MSRS class version. -- @field #string version -MSRS.version="0.1.3" +MSRS.version="0.2.0" --- Voices -- @type MSRS.Voices @@ -304,7 +314,20 @@ MSRS.Voices = { }, } ---- +--- Text-to-speech providers. These are compatible with the DCS-gRPC conventions. +-- @type MSRS.Provider +-- @field #string WINDOWS Microsoft windows (`win`). +-- @field #string GOOGLE Google (`gcloud`). +-- @field #string AZURE Microsoft Azure (`azure`). +-- @field #string AMAZON Amazon Web Service (`asw`). +MSRS.Provider = { + WINDOWS = "win", + GOOGLE = "gcloud", + AZURE = "azure", + AMAZON = "asw", +} + +--- Provider options. -- @type MSRS.ProviderOptions -- @field #string key -- @field #string secret @@ -312,7 +335,7 @@ MSRS.Voices = { -- @field #string defaultVoice -- @field #string voice ---- GRPC options +--- GRPC options. -- @type MSRS.GRPCOptions -- @field #string plaintext -- @field #string srsClientName @@ -339,6 +362,8 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Refactoring of input/config file. +-- TODO: Refactoring gRPC backend. -- TODO: Add functions to remove freqs and modulations. -- DONE: Add coordinate. -- DONE: Add google. @@ -354,7 +379,7 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. --- @param #number Volume Volume - 1.0 is max, 0.0 is silence +-- @param #number Volume Volume - 1.0 is max, 0.0 is silence. -- @param #table AltBackend Optional table containing tables 'Functions' and 'Vars' which add/replace functions and variables for the MSRS instance to allow alternate backends for transmitting to SRS. -- @return #MSRS self function MSRS:New(PathToSRS, Frequency, Modulation, Volume, AltBackend) @@ -534,13 +559,8 @@ end -- @param #table Frequencies Frequencies in MHz. Can also be given as a #number if only one frequency should be used. -- @return #MSRS self function MSRS:SetFrequencies(Frequencies) - - -- Ensure table. - if type(Frequencies)~="table" then - Frequencies={Frequencies} - end - self.frequencies=Frequencies + self.frequencies=UTILS.EnsureTable(Frequencies, false) return self end @@ -551,12 +571,8 @@ end -- @return #MSRS self function MSRS:AddFrequencies(Frequencies) - -- Ensure table. - if type(Frequencies)~="table" then - Frequencies={Frequencies} - end - - for _,_freq in pairs(Frequencies) do + for _,_freq in pairs(UTILS.EnsureTable(Frequencies, false)) do + self:T(self.lid..string.format("Adding frequency %s", tostring(_freq))) table.insert(self.frequencies,_freq) end @@ -576,13 +592,12 @@ end -- @param #table Modulations Modulations. Can also be given as a #number if only one modulation should be used. -- @return #MSRS self function MSRS:SetModulations(Modulations) - - -- Ensure table. - if type(Modulations)~="table" then - Modulations={Modulations} - end - self.modulations=Modulations + self.modulations=UTILS.EnsureTable(Modulations, false) + + -- Debug info. + self:T(self.lid.."Modulations:") + self:T(self.modulations) return self end @@ -592,13 +607,8 @@ end -- @param #table Modulations Modulations. Can also be given as a #number if only one modulation should be used. -- @return #MSRS self function MSRS:AddModulations(Modulations) - - -- Ensure table. - if type(Modulations)~="table" then - Modulations={Modulations} - end - for _,_mod in pairs(Modulations) do + for _,_mod in pairs(UTILS.EnsureTable(Modulations, false)) do table.insert(self.modulations,_mod) end From af3c579a03a4f75e74d84ec344a17d28af5bb0c7 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 23 Nov 2023 22:22:59 +0100 Subject: [PATCH 02/10] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 346 +++++++++++++++++++------- 1 file changed, 258 insertions(+), 88 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 196729143..dba599830 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -3,7 +3,8 @@ -- === -- -- **Main Features:** --- +-- +-- * Incease immersion of your missions with more sound output -- * Play sound files via SRS -- * Play text-to-speech via SRS -- @@ -113,7 +114,7 @@ -- **Pro-Tipp** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing. -- and also the Google Console error, in case you have missed a step in setting up your Google TTS. -- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` --- Plays a message on 255AM for the blue coalition in-game. +-- Plays a message on 255 MHz AM for the blue coalition in-game. -- -- ## Set Voice -- @@ -314,6 +315,16 @@ MSRS.Voices = { }, } + +--- Backend options to communicate with SRS. +-- @type MSRS.Backend +-- @field #string SRSEXE Use SRS exe. +-- @field #string GRPC Use DCS-gRPC. +MSRS.Backend = { + SRSEXE = "srsexe", + GRPC = "grpc", +} + --- Text-to-speech providers. These are compatible with the DCS-gRPC conventions. -- @type MSRS.Provider -- @field #string WINDOWS Microsoft windows (`win`). @@ -374,7 +385,10 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- Constructor ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Create a new MSRS object. +--- Create a new MSRS object. Required argument is the frequency and modulation. +-- Other parameters are read from the `Moose_MSRS.lua` config file. If you do not have that file set up you must set up and use the SRS-TTS.exe (not DCS-gRPC) as backend, you need to still +-- set the path to the exe file via @{#MSRS.SetPath}. +-- -- @param #MSRS self -- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. @@ -382,14 +396,16 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- @param #number Volume Volume - 1.0 is max, 0.0 is silence. -- @param #table AltBackend Optional table containing tables 'Functions' and 'Vars' which add/replace functions and variables for the MSRS instance to allow alternate backends for transmitting to SRS. -- @return #MSRS self -function MSRS:New(PathToSRS, Frequency, Modulation, Volume, AltBackend) +function MSRS:New(Frequency, Modulation, Volume, AltBackend) -- Defaults. Frequency = Frequency or 143 Modulation = Modulation or radio.modulation.AM - -- Inherit everything from FSM class. + -- Inherit everything from BASE class. local self=BASE:Inherit(self, BASE:New()) -- #MSRS + + self.lid = string.format("%s-%s | ", "unknown", self.version) -- If AltBackend is supplied, initialize it, which will add/replace functions and variables in this MSRS instance. if type( AltBackend ) == "table" or type( self.AltBackend ) == "table" then @@ -452,6 +468,22 @@ end -- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Set backend to communicate with SRS. +-- There are two options: +-- +-- - `MSRS.Backend.SRSEXE`: This is the default and uses the SRS.exe. +-- - `MSRS.Backend.GRPC`: Via DCS-gRPC. +-- +-- @param #MSRS self +-- @param #string Backend Backend used. Default is `MSRS.Backend.SRSEXE`. +-- @return #MSRS self +function MSRS:SetBackend(Backend) + + self.backend=Backend or MSRS.Backend.SRSEXE + + return self +end + --- Set path to SRS install directory. More precisely, path to where the DCS- -- @param #MSRS self -- @param #string Path Path to the directory, where the sound file is located. This does **not** contain a final backslash or slash. @@ -649,7 +681,7 @@ function MSRS:SetCulture(Culture) return self end ---- Set to use a specific voice. Will override gender and culture settings. +--- Set to use a specific voice. Will override any gender and culture settings. -- @param #MSRS self -- @param #string Voice Voice. -- @return #MSRS self @@ -993,12 +1025,105 @@ function MSRS:_NewAltBackend(Backend) return self end + +--- Get lat, long and alt from coordinate. +-- @param #MSRS self +-- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. +-- @return #number Latitude. +-- @return #number Longitude. +-- @return #number Altitude. +function MSRS:_GetLatLongAlt(Coordinate) + + local lat, lon, alt=coord.LOtoLL(Coordinate) + + return lat, lon, math.floor(alt) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Backend ExternalAudio.exe +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get SRS command to play sound using the `DCS-SR-ExternalAudio.exe`. +-- @param #MSRS self +-- @param #table freqs Frequencies in MHz. +-- @param #table modus Modulations. +-- @param #number coal Coalition. +-- @param #string gender Gender. +-- @param #string voice Voice. +-- @param #string culture Culture. +-- @param #number volume Volume. +-- @param #number speed Speed. +-- @param #number port Port. +-- @param #string label Label, defaults to "ROBOT" (displayed sender name in the radio overlay of SRS) - No spaces allowed! +-- @param Core.Point#COORDINATE coordinate Coordinate. +-- @return #string Command. +function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port, label, coordinate) + + local path=self:GetPath() or STTS.DIRECTORY + + local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" + + freqs=table.concat(freqs or self.frequencies, ",") + modus=table.concat(modus or self.modulations, ",") + + coal=coal or self.coalition + gender=gender or self.gender + voice=voice or self.voice + culture=culture or self.culture + volume=volume or self.volume + speed=speed or self.speed + port=port or self.port + label=label or self.Label + coordinate=coordinate or self.coordinate + + -- Replace modulation + modus=modus:gsub("0", "AM") + modus=modus:gsub("1", "FM") + + -- Command. + local command=string.format('"%s\\%s" -f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume) + + -- Set voice or gender/culture. + if voice then + -- Use a specific voice (no need for gender and/or culture. + command=command..string.format(" --voice=\"%s\"", tostring(voice)) + else + -- Add gender. + if gender and gender~="female" then + command=command..string.format(" -g %s", tostring(gender)) + end + -- Add culture. + if culture and culture~="en-GB" then + command=command..string.format(" -l %s", tostring(culture)) + end + end + + -- Set coordinate. + if coordinate then + local lat,lon,alt=self:_GetLatLongAlt(coordinate) + command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt) + end + + -- Set google. + if self.google and self.ttsprovider == "Google" then + command=command..string.format(' --ssml -G "%s"', self.google) + end + + -- Debug output. + self:T("MSRS command="..command) + + return command +end + --- Execute SRS command to play sound using the `DCS-SR-ExternalAudio.exe`. -- @param #MSRS self -- @param #string command Command to executer -- @return #number Return value of os.execute() command. function MSRS:_ExecCommand(command) + + -- Debug info. self:T("SRS TTS command="..command) + -- Create a tmp file. local filename=os.getenv('TMP').."\\MSRS-"..STTS.uuid()..".bat" @@ -1064,7 +1189,7 @@ function MSRS:_ExecCommand(command) res=os.execute(command) -- Remove file in 1 second. - timer.scheduleFunction(os.remove, filename, timer.getTime()+1) + timer.scheduleFunction(os.remove, filename, timer.getTime()+1) end @@ -1072,88 +1197,133 @@ function MSRS:_ExecCommand(command) return res end ---- Get lat, long and alt from coordinate. +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- DCS-gRPC Backend Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- DCS-gRPC v0.70 TTS API call: +-- GRPC.tts(ssml, frequency[, options]) - Synthesize text (ssml; SSML tags supported) to speech and transmit it over SRS on the frequency with the following optional options (and their defaults): + +-- { +-- -- The plain text without any transformations made to it for the purpose of getting it spoken out +-- -- as desired (no SSML tags, no FOUR NINER instead of 49, ...). Even though this field is +-- -- optional, please consider providing it as it can be used to display the spoken text to players +-- -- with hearing impairments. +-- plaintext = null, -- e.g. `= "Hello Pilot"` + +-- -- Name of the SRS client. +-- srsClientName = "DCS-gRPC", + +-- -- The origin of the transmission. Relevant if the SRS server has "Line of +-- -- Sight" and/or "Distance Limit" enabled. +-- position = { +-- lat = 0.0, +-- lon = 0.0, +-- alt = 0.0, -- in meters +-- }, + +-- -- The coalition of the transmission. Relevant if the SRS server has "Secure +-- -- Coalition Radios" enabled. Supported values are: `blue` and `red`. Defaults +-- -- to being spectator if not specified. +-- coalition = null, + +-- -- TTS provider to be use. Defaults to the one configured in your config or to Windows' +-- -- built-in TTS. Examples: +-- -- `= { aws = {} }` / `= { aws = { voice = "..." } }` enable AWS TTS +-- -- `= { azure = {} }` / `= { azure = { voice = "..." } }` enable Azure TTS +-- -- `= { gcloud = {} }` / `= { gcloud = { voice = "..." } }` enable Google Cloud TTS +-- -- `= { win = {} }` / `= { win = { voice = "..." } }` enable Windows TTS +-- provider = null, +-- } + +--- Make DCS-gRPC API call to transmit text-to-speech over SRS. -- @param #MSRS self --- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. --- @return #number Latitude. --- @return #number Longitude. --- @return #number Altitude. -function MSRS:_GetLatLongAlt(Coordinate) - - local lat, lon, alt=coord.LOtoLL(Coordinate) - - return lat, lon, math.floor(alt) +-- @param #string Text Text of message to transmit (can also be SSML). +-- @param #string Optional plaintext version of message (for accessiblity). +-- @param #table Frequencies Radio frequencies to transmit on. Can also accept a number in MHz. +-- @param #string Voice Voice for the TTS provider to user. +-- @param #string Label Label (SRS diplays as name of the transmitter). +-- @return #MSRS self +function MSRS:_DCSgRPCtts(Text, Plaintext, Frequencies, Voice, Label) + + BASE:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") + BASE:T({Text, Plaintext, Frequencies, Voice, Label}) + + local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions + local ssml = Text or '' + + local XmitFrequencies = Frequencies or self.Frequency + if type(XmitFrequencies)~="table" then + XmitFrequencies={XmitFrequencies} + end + + options.plaintext = Plaintext + options.srsClientName = Label or self.Label + options.position = {} + if self.coordinate then + options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) + end + + options.position.lat = options.position.lat or 0.0 + options.position.lon = options.position.lon or 0.0 + options.position.alt = options.position.alt or 0.0 + + if UTILS.GetCoalitionName(self.coalition) == 'Blue' then + options.coalition = 'blue' + elseif UTILS.GetCoalitionName(self.coalition) == 'Red' then + options.coalition = 'red' + end + + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider + + options.provider = {} + + options.provider[provider] = {} + + if self.APIKey then + options.provider[provider].key = self.APIKey + end + + if self.defaultVoice then + options.provider[provider].defaultVoice = self.defaultVoice + end + + if self.voice then + options.provider[provider].voice = Voice or self.voice or self.defaultVoice + elseif ssml then + -- DCS-gRPC doesn't directly support language/gender, but can use SSML + -- Only use if a voice isn't explicitly set + local preTag, genderProp, langProp, postTag = '', '', '', '' + + if self.gender then + genderProp = ' gender=\"' .. self.gender .. '\"' + end + if self.culture then + langProp = ' language=\"' .. self.culture .. '\"' + end + + if self.culture or self.gender then + preTag = '' + postTag = '' + ssml = preTag .. Text .. postTag + end + + end + + for _,_freq in ipairs(XmitFrequencies) do + local freq = _freq*1000000 + BASE:T("GRPC.tts") + BASE:T(ssml) + BASE:T(freq) + BASE:T({options}) + GRPC.tts(ssml, freq, options) + end + end - ---- Get SRS command to play sound using the `DCS-SR-ExternalAudio.exe`. --- @param #MSRS self --- @param #table freqs Frequencies in MHz. --- @param #table modus Modulations. --- @param #number coal Coalition. --- @param #string gender Gender. --- @param #string voice Voice. --- @param #string culture Culture. --- @param #number volume Volume. --- @param #number speed Speed. --- @param #number port Port. --- @param #string label Label, defaults to "ROBOT" (displayed sender name in the radio overlay of SRS) - No spaces allowed! --- @param Core.Point#COORDINATE coordinate Coordinate. --- @return #string Command. -function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port,label,coordinate) - - local path=self:GetPath() or STTS.DIRECTORY - local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" - freqs=table.concat(freqs or self.frequencies, ",") - modus=table.concat(modus or self.modulations, ",") - coal=coal or self.coalition - gender=gender or self.gender - voice=voice or self.voice - culture=culture or self.culture - volume=volume or self.volume - speed=speed or self.speed - port=port or self.port - label=label or self.Label - coordinate=coordinate or self.coordinate - - -- Replace modulation - modus=modus:gsub("0", "AM") - modus=modus:gsub("1", "FM") - - -- Command. - local command=string.format('"%s\\%s" -f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume) - - -- Set voice or gender/culture. - if voice then - -- Use a specific voice (no need for gender and/or culture. - command=command..string.format(" --voice=\"%s\"", tostring(voice)) - else - -- Add gender. - if gender and gender~="female" then - command=command..string.format(" -g %s", tostring(gender)) - end - -- Add culture. - if culture and culture~="en-GB" then - command=command..string.format(" -l %s", tostring(culture)) - end - end - - -- Set coordinate. - if coordinate then - local lat,lon,alt=self:_GetLatLongAlt(coordinate) - command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt) - end - - -- Set google. - if self.google and self.ttsprovider == "Google" then - command=command..string.format(' --ssml -G "%s"', self.google) - end - - -- Debug output. - self:T("MSRS command="..command) - - return command -end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Config File +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Get central SRS configuration to be able to play tts over SRS radio using the `DCS-SR-ExternalAudio.exe`. -- @param #MSRS self @@ -1208,7 +1378,7 @@ end -- This will populate variables for the MSRS raw class and all instances you create with e.g. `mysrs = MSRS:New()` -- Optionally you can also load this per **single instance** if so needed, i.e. -- --- mysrs:LoadConfigFile(Path,Filename) +-- mysrs:LoadConfigFile(Path,Filename) -- -- 4) Use the config in your code like so, variable names are basically the same as in the config file, but all lower case, examples: -- @@ -1225,7 +1395,7 @@ end -- atis:SetSRS(nil,nil,nil,MSRS.Voices.Google.Standard.en_US_Standard_H) -- --Start ATIS -- atis:Start() -function MSRS:LoadConfigFile(Path,Filename) +function MSRS:LoadConfigFile(Path, Filename) local path = Path or lfs.writedir()..MSRS.ConfigFilePath local file = Filename or MSRS.ConfigFileName or "Moose_MSRS.lua" From 1b1f8e0d2c2138fb3082e55ffb358927ed1e4244 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 24 Nov 2023 23:59:31 +0100 Subject: [PATCH 03/10] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 371 ++++++++++++++++---------- 1 file changed, 234 insertions(+), 137 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 4e762d8b2..f9ff9e0bf 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -65,6 +65,7 @@ -- ## Prerequisites -- -- This script needs SRS version >= 1.9.6. +-- Optional: DCS-gRPC as backend to communicate with SRS (vide infra) -- -- ## Knwon Issues -- @@ -130,6 +131,7 @@ -- ## Set Coordinate -- -- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted. +-- Note that this is only a factor if SRS server has line-of-sight and/or distance limit enabled. -- -- ## Set SRS Port -- @@ -193,6 +195,7 @@ MSRS = { lid = nil, port = 5002, name = "MSRS", + backend = "srsexe", frequencies = {}, modulations = {}, coalition = 0, @@ -202,12 +205,12 @@ MSRS = { volume = 1, speed = 1, coordinate = nil, + provider = "win", Label = "ROBOT", - AltBackend = nil, ConfigFileName = "Moose_MSRS.lua", ConfigFilePath = "Config\\", ConfigLoaded = false, - ttsprovider = "Microsoft", + poptions = {}, } --- MSRS class version. @@ -329,8 +332,8 @@ MSRS.Backend = { -- @type MSRS.Provider -- @field #string WINDOWS Microsoft windows (`win`). -- @field #string GOOGLE Google (`gcloud`). --- @field #string AZURE Microsoft Azure (`azure`). --- @field #string AMAZON Amazon Web Service (`asw`). +-- @field #string AZURE Microsoft Azure (`azure`). Only possible with DCS-gRPC backend. +-- @field #string AMAZON Amazon Web Service (`asw`). Only possible with DCS-gRPC backend. MSRS.Provider = { WINDOWS = "win", GOOGLE = "gcloud", @@ -340,10 +343,10 @@ MSRS.Provider = { --- Provider options. -- @type MSRS.ProviderOptions --- @field #string key --- @field #string secret --- @field #string region --- @field #string defaultVoice +-- @field #string key Access key (DCS-gRPC with Google, ASW, AZURE as provider). +-- @field #string secret Secret key (DCS-gRPC with ASW as provider) +-- @field #string region Region. +-- @field #string defaultVoice Default voice. -- @field #string voice --- GRPC options. @@ -390,13 +393,11 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- set the path to the exe file via @{#MSRS.SetPath}. -- -- @param #MSRS self --- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. --- @param #number Volume Volume - 1.0 is max, 0.0 is silence. --- @param #table AltBackend Optional table containing tables 'Functions' and 'Vars' which add/replace functions and variables for the MSRS instance to allow alternate backends for transmitting to SRS. +-- @param #string Backend Backend used: `MSRS.Backend.SRSEXE` (default) or `MSRS.Backend.GRPC`. -- @return #MSRS self -function MSRS:New(Frequency, Modulation, Volume, AltBackend) +function MSRS:New(Frequency, Modulation, Backend) -- Defaults. Frequency = Frequency or 143 @@ -407,52 +408,27 @@ function MSRS:New(Frequency, Modulation, Volume, AltBackend) self.lid = string.format("%s-%s | ", "unknown", self.version) - -- If AltBackend is supplied, initialize it, which will add/replace functions and variables in this MSRS instance. - if type( AltBackend ) == "table" or type( self.AltBackend ) == "table" then - - local Backend = UTILS.DeepCopy(AltBackend) or UTILS.DeepCopy(self.AltBackend) - - -- Add parameters to vars so alternate backends can use them if applicable - Backend.Vars = Backend.Vars or {} - Backend.Vars.PathToSRS = PathToSRS - Backend.Vars.Frequency = UTILS.DeepCopy(Frequency) - Backend.Vars.Modulation = UTILS.DeepCopy(Modulation) - Backend.Vars.Volume = Volume - - Backend.Functions = Backend.Functions or {} - - return self:_NewAltBackend(Backend) - end - if not self.ConfigLoaded then - -- If no AltBackend table, the proceed with default initialisation - self:SetPath(PathToSRS) + -- Defaults. + self:SetPath() self:SetPort() self:SetFrequencies(Frequency) self:SetModulations(Modulation) self:SetGender() self:SetCoalition() self:SetLabel() - self:SetVolume(Volume) + self:SetVolume() else -- there might be some overwrites from :New() - if PathToSRS then - self:SetPath(PathToSRS) - end - if Frequency then self:SetFrequencies(Frequency) self:SetModulations(Modulation) end - if Volume then - self:SetVolume(Volume) - end - end self.lid = string.format("%s-%s | ", self.name, self.version) @@ -484,9 +460,37 @@ function MSRS:SetBackend(Backend) return self end +--- Set DCS-gRPC as backend to communicate with SRS. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetBackendGRPC() + + self:SetBackend(MSRS.Backend.GRPC) + + return self +end + +--- Set SRS-TTS.exe as backend to communicate with SRS. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetBackendSRSEXE(Backend) + + self:SetBackend(MSRS.Backend.SRSEXE) + + return self +end + + +--- Get currently set backend. +-- @param #MSRS self +-- @return #string Backend. +function MSRS:GetBackend() + return self.backend +end + --- Set path to SRS install directory. More precisely, path to where the DCS- -- @param #MSRS self --- @param #string Path Path to the directory, where the sound file is located. This does **not** contain a final backslash or slash. +-- @param #string Path Path to the directory, where the sound file is located. -- @return #MSRS self function MSRS:SetPath(Path) @@ -613,7 +617,7 @@ end --- Get frequencies. -- @param #MSRS self --- @param #table Frequencies in MHz. +-- @return #table Frequencies in MHz. function MSRS:GetFrequencies() return self.frequencies end @@ -672,7 +676,7 @@ end --- Set culture. -- @param #MSRS self --- @param #string Culture Culture, e.g. "en-GB" (default). +-- @param #string Culture Culture, e.g. "en-GB". -- @return #MSRS self function MSRS:SetCulture(Culture) @@ -681,7 +685,7 @@ function MSRS:SetCulture(Culture) return self end ---- Set to use a specific voice. Will override any gender and culture settings. +--- Set to use a specific voice. Note that this will override any gender and culture settings as a voice already has a certain gender/culture. -- @param #MSRS self -- @param #string Voice Voice. -- @return #MSRS self @@ -689,10 +693,6 @@ function MSRS:SetVoice(Voice) self.voice=Voice - --local defaultprovider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider or "win" - - --self.GRPCOptions[defaultprovider].voice = Voice - return self end @@ -703,13 +703,14 @@ end function MSRS:SetDefaultVoice(Voice) self.defaultVoice=Voice + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider or "win" self.GRPCOptions[provider].defaultVoice = Voice return self end ---- Set the coordinate from which the transmissions will be broadcasted. +--- Set the coordinate from which the transmissions will be broadcasted. Note that this is only a factor if SRS has line-of-sight or distance enabled. -- @param #MSRS self -- @param Core.Point#COORDINATE Coordinate Origin of the transmission. -- @return #MSRS self @@ -755,6 +756,77 @@ function MSRS:SetGoogleAPIKey(APIKey) return self end + +--- Set provider used to generate text-to-speech. +-- These options are available: +-- +-- - `MSRS.Provider.WINDOWS`: Microsoft Windows (default) +-- - `MSRS.Provider.GOOGLE`: Google Cloud +-- - `MSRS.Provider.AZURE`: Microsoft Azure (only with DCS-gRPC backend) +-- - `MSRS.Provier.AMAZON`: Amazone Web Service (only with DCS-gRPC backend) +-- +-- Note that all providers except Microsoft Windows need as additonal information the credentials of your account. +-- +-- @param #MSRS self +-- @param #string Provider +-- @return #MSRS self +function MSRS:SetProvider(Provider) + self.provider = Provider or MSRS.Provider.WINDOWS + return self +end + + +--- Get provider. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:GetProvider() + return self.provider or MSRS.Provider.WINDOWS +end + +--- Set provider options and credentials. +-- @param #MSRS self +-- @param #string Provider Provider. +-- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. +-- @param #string AccessKey Your API access key. +-- @param #string SecretKey Your secret key. +-- @param #string Region Region to use. +-- @return #MSRS.ProviderOptions Provider optionas table. +function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) + + local option={} --#MSRS.ProviderOptions + + option.provider=Provider + option.credentials=CredentialsFile + option.key=AccessKey + option.secret=SecretKey + option.region=Region + + self.poptions[Provider]=option + + return option +end + +--- Set provider options and credentials for Google Cloud. +-- @param #MSRS self +-- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. This is used if SRS-TTS.exe is used as backend. +-- @param #string AccessKey Your API access key. This is necessary if DCS-gRPC is used as backend. +-- @return #MSRS self +function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) + + self:SetProviderOptions(MSRS.Provider.GOOGLE, CredentialsFile, AccessKey) + + return self +end + +--- Set provider options and credentials for Google Cloud. +-- @param #MSRS self +-- @param #string Provider Provider. Default is as set via @{#MSRS.SetProvider}. +-- @return #MSRS.ProviderOptions Provider options. +function MSRS:GetProviderOptions(Provider) + return self.poptions[Provider or self.provider] +end + + --- Use Google text-to-speech as default. -- @param #MSRS self -- @return #MSRS self @@ -894,15 +966,24 @@ function MSRS:PlayText(Text, Delay, Coordinate) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate) else + + if self.backend==MSRS.Backend.SRSEXE then - -- Get command line. - local command=self:_GetCommand(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,Coordinate) - - -- Append text. - command=command..string.format(" --text=\"%s\"", tostring(Text)) - - -- Execute command. - self:_ExecCommand(command) + -- Get command line. + local command=self:_GetCommand(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,Coordinate) + + -- Append text. + command=command..string.format(" --text=\"%s\"", tostring(Text)) + + -- Execute command. + self:_ExecCommand(command) + + else + + self:T(self.lid.."Transmitting") + self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate) + + end end @@ -927,26 +1008,24 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) else + + if self.backend==MSRS.Backend.SRSEXE then - -- Ensure table. - if Frequencies and type(Frequencies)~="table" then - Frequencies={Frequencies} + -- Get command line. + local command=self:_GetCommand(UTILS.EnsureTable(Frequencies, false), UTILS.EnsureTable(Modulations, false), nil, Gender, Voice, Culture, Volume, nil, nil, Label, Coordinate) + + -- Append text. + command=command..string.format(" --text=\"%s\"", tostring(Text)) + + -- Execute command. + self:_ExecCommand(command) + + elseif self.backend==MSRS.Backend.GRPC then + + self:_DCSgRPCtts(Text,Optional,Frequencies,Voice,Label,Plaintext) + end - -- Ensure table. - if Modulations and type(Modulations)~="table" then - Modulations={Modulations} - end - - -- Get command line. - local command=self:_GetCommand(Frequencies, Modulations, nil, Gender, Voice, Culture, Volume, nil, nil, Label, Coordinate) - - -- Append text. - command=command..string.format(" --text=\"%s\"", tostring(Text)) - - -- Execute command. - self:_ExecCommand(command) - end return self @@ -1029,12 +1108,18 @@ end --- Get lat, long and alt from coordinate. -- @param #MSRS self -- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. --- @return #number Latitude. --- @return #number Longitude. --- @return #number Altitude. +-- @return #number Latitude (or 0 if no input coordinate was given). +-- @return #number Longitude (or 0 if no input coordinate was given). +-- @return #number Altitude (or 0 if no input coordinate was given). function MSRS:_GetLatLongAlt(Coordinate) - local lat, lon, alt=coord.LOtoLL(Coordinate) + local lat=0.0 + local lon=0.0 + local alt=0.0 + + if Coordinate then + lat, lon, alt=coord.LOtoLL(Coordinate) + end return lat, lon, math.floor(alt) end @@ -1239,85 +1324,92 @@ end --- Make DCS-gRPC API call to transmit text-to-speech over SRS. -- @param #MSRS self -- @param #string Text Text of message to transmit (can also be SSML). --- @param #string Optional plaintext version of message (for accessiblity). -- @param #table Frequencies Radio frequencies to transmit on. Can also accept a number in MHz. --- @param #string Voice Voice for the TTS provider to user. --- @param #string Label Label (SRS diplays as name of the transmitter). +-- @param #string Gender Gender. +-- @param #string Culture Culture. +-- @param #string Voice Voice. +-- @param #number Volume Volume. +-- @param #string Label Label. +-- @param Core.Point#COORDINATE Coordinate Coordinate. -- @return #MSRS self -function MSRS:_DCSgRPCtts(Text, Plaintext, Frequencies, Voice, Label) +function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) - BASE:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") - BASE:T({Text, Plaintext, Frequencies, Voice, Label}) - - local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions - local ssml = Text or '' - - local XmitFrequencies = Frequencies or self.Frequency - if type(XmitFrequencies)~="table" then - XmitFrequencies={XmitFrequencies} - end - - options.plaintext = Plaintext - options.srsClientName = Label or self.Label + --Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) + + self:I("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") + self:I({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) + + --local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions + + local options = {} -- #MSRS.GRPCOptions + + local ssml = Text or '' + + Frequencies = UTILS.EnsureTable(Frequencies, true) or self:GetFrequencies() + + options.plaintext = Plaintext + options.srsClientName = Label or self.Label + + if false then + options.position = {} if self.coordinate then - options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) - end - - options.position.lat = options.position.lat or 0.0 - options.position.lon = options.position.lon or 0.0 - options.position.alt = options.position.alt or 0.0 - + options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) + end + if UTILS.GetCoalitionName(self.coalition) == 'Blue' then options.coalition = 'blue' elseif UTILS.GetCoalitionName(self.coalition) == 'Red' then options.coalition = 'red' end - + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider - + options.provider = {} - - options.provider[provider] = {} - + --options.provider[provider] = {} + options.provider[provider] = self:GetProviderOptions(provider) + if self.APIKey then options.provider[provider].key = self.APIKey end - + if self.defaultVoice then options.provider[provider].defaultVoice = self.defaultVoice end + + end - if self.voice then - options.provider[provider].voice = Voice or self.voice or self.defaultVoice - elseif ssml then - -- DCS-gRPC doesn't directly support language/gender, but can use SSML - -- Only use if a voice isn't explicitly set - local preTag, genderProp, langProp, postTag = '', '', '', '' - - if self.gender then - genderProp = ' gender=\"' .. self.gender .. '\"' - end - if self.culture then - langProp = ' language=\"' .. self.culture .. '\"' - end - - if self.culture or self.gender then - preTag = '' - postTag = '' - ssml = preTag .. Text .. postTag - end - + if self.voice then + --options.provider[provider].voice = Voice or self.voice or self.defaultVoice + elseif ssml then + -- DCS-gRPC doesn't directly support language/gender, but can use SSML + -- Only use if a voice isn't explicitly set + local preTag, genderProp, langProp, postTag = '', '', '', '' + + if self.gender then + genderProp = ' gender=\"' .. self.gender .. '\"' end - - for _,_freq in ipairs(XmitFrequencies) do - local freq = _freq*1000000 - BASE:T("GRPC.tts") - BASE:T(ssml) - BASE:T(freq) - BASE:T({options}) - GRPC.tts(ssml, freq, options) + if self.culture then + langProp = ' language=\"' .. self.culture .. '\"' end + + if self.culture or self.gender then + preTag = '' + postTag = '' + ssml = preTag .. Text .. postTag + end + end + + env.info("FF freq") + for _,freq in pairs(Frequencies) do + env.info("FF freq1") + self:T("GRPC.tts") + self:T(ssml) + self:T(freq) + self:T({options}) + UTILS.PrintTableToLog(options) + --GRPC.tts(ssml, freq*1e6, options) + end end @@ -1778,10 +1870,15 @@ MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Fr end if self.voice then + + -- Set voice. options.provider[provider].voice = Voice or self.voice or self.defaultVoice + elseif ssml then + -- DCS-gRPC doesn't directly support language/gender, but can use SSML -- Only use if a voice isn't explicitly set + local preTag, genderProp, langProp, postTag = '', '', '', '' if self.gender then @@ -2200,7 +2297,7 @@ function MSRSQUEUE:_CheckRadioQueue(delay) end -MSRS.LoadConfigFile() +--MSRS.LoadConfigFile() ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 67924c894d4944daae8adbb23ea69bbf012dab9f Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 26 Nov 2023 16:45:19 +0100 Subject: [PATCH 04/10] Update SRS.lua - Removed altbackend functions --- Moose Development/Moose/Sound/SRS.lua | 508 ++++---------------------- 1 file changed, 73 insertions(+), 435 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index f9ff9e0bf..c85ddcd9b 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -801,6 +801,8 @@ function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey option.secret=SecretKey option.region=Region + self.poptions=self.poptions or {} + self.poptions[Provider]=option return option @@ -940,15 +942,23 @@ function MSRS:PlaySoundText(SoundText, Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlaySoundText, self, SoundText, 0) else + + if self.backend==MSRS.Backend.GRPC then + + self:_DCSgRPCtts(SoundText.text, nil, SoundText.gender, SoundText.culture, SoundText.voice, SoundText.volume, SoundText.label, SoundText.coordinate) + + else - -- Get command. - local command=self:_GetCommand(nil, nil, nil, SoundText.gender, SoundText.voice, SoundText.culture, SoundText.volume, SoundText.speed) - - -- Append text. - command=command..string.format(" --text=\"%s\"", tostring(SoundText.text)) - - -- Execute command. - self:_ExecCommand(command) + -- Get command. + local command=self:_GetCommand(nil, nil, nil, SoundText.gender, SoundText.voice, SoundText.culture, SoundText.volume, SoundText.speed) + + -- Append text. + command=command..string.format(" --text=\"%s\"", tostring(SoundText.text)) + + -- Execute command. + self:_ExecCommand(command) + + end end @@ -967,23 +977,16 @@ function MSRS:PlayText(Text, Delay, Coordinate) self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate) else - if self.backend==MSRS.Backend.SRSEXE then - - -- Get command line. - local command=self:_GetCommand(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,Coordinate) - - -- Append text. - command=command..string.format(" --text=\"%s\"", tostring(Text)) - - -- Execute command. - self:_ExecCommand(command) - - else - + if self.backend==MSRS.Backend.GRPC then + self:T(self.lid.."Transmitting") self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate) - end + else + + self:PlayTextExt(Text,Delay, nil, nil, nil, nil, nil, nil, nil,Coordinate) + + end end @@ -1022,7 +1025,7 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture elseif self.backend==MSRS.Backend.GRPC then - self:_DCSgRPCtts(Text,Optional,Frequencies,Voice,Label,Plaintext) + self:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) end @@ -1075,36 +1078,6 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Adds or replaces functions and variables in the current MSRS class instance to enable an alternate backends for transmitting to SRS. --- @param #MSRS self --- @param #table A table containing a table `Functions` with new/replacement class functions and `Vars` with new/replacement variables. --- @return #MSRS self -function MSRS:_NewAltBackend(Backend) - BASE:T('Entering MSRS:_NewAltBackend()') - - -- Add/replace class instance functions with those defined in the alternate backend in Functions table - for funcName,funcDef in pairs(Backend.Functions) do - if type(funcDef) == 'function' then - BASE:T('MSRS (re-)defining function MSRS:' .. funcName ) - self[funcName] = funcDef - end - end - - -- Add/replace class instance variables with those defined in the alternate backend in Vars table - for varName,varVal in pairs(Backend.Vars) do - BASE:T('MSRS setting self.' .. varName) - self[varName] = UTILS.DeepCopy(varVal) - end - - -- If _MSRSbackendInit() is defined in the backend, then run it (it should return self) - if self._MSRSbackendInit and type(self._MSRSbackendInit) == 'function' then - return self:_MSRSbackendInit() - end - - return self -end - - --- Get lat, long and alt from coordinate. -- @param #MSRS self -- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. @@ -1345,59 +1318,62 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab local ssml = Text or '' + -- Get frequenceies. Frequencies = UTILS.EnsureTable(Frequencies, true) or self:GetFrequencies() - options.plaintext = Plaintext + -- Plain text (not really used. + options.plaintext=Text + + -- Name shows as sender. options.srsClientName = Label or self.Label - - if false then - - options.position = {} - if self.coordinate then - options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) - end - - if UTILS.GetCoalitionName(self.coalition) == 'Blue' then - options.coalition = 'blue' - elseif UTILS.GetCoalitionName(self.coalition) == 'Red' then - options.coalition = 'red' - end - - local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider - - options.provider = {} - --options.provider[provider] = {} - options.provider[provider] = self:GetProviderOptions(provider) - - if self.APIKey then - options.provider[provider].key = self.APIKey - end - - if self.defaultVoice then - options.provider[provider].defaultVoice = self.defaultVoice - end - - end - if self.voice then - --options.provider[provider].voice = Voice or self.voice or self.defaultVoice - elseif ssml then + -- Set position. + if self.coordinate then + options.position = {} + options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) + end + + -- Coalition (gRPC expects lower case) + options.coalition = UTILS.GetCoalitionName(self.coalition):lower() + + -- Provider (win, gcloud, ...) + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider + + -- Provider options: voice, credentials + options.provider = {} + options.provider[provider] = self:GetProviderOptions(provider) + + -- Voice + Voice=Voice or self.voice + + if Voice then + -- We use a specific voice + options.provider[provider].voice = Voice + else -- DCS-gRPC doesn't directly support language/gender, but can use SSML - -- Only use if a voice isn't explicitly set + local preTag, genderProp, langProp, postTag = '', '', '', '' + local gender="" if self.gender then - genderProp = ' gender=\"' .. self.gender .. '\"' + --gender = ' gender=\"' .. self.gender .. '\"' + gender=string.format(' gender=\"\%s\"', self.gender) end + local language="" if self.culture then - langProp = ' language=\"' .. self.culture .. '\"' + --lang = ' language=\"' .. self.culture .. '\"' + language=string.format(' language=\"\%s\"', self.culture) end - if self.culture or self.gender then - preTag = '' - postTag = '' - ssml = preTag .. Text .. postTag - end +-- if self.culture or self.gender then +-- preTag = '' +-- postTag = '' +-- ssml = preTag .. Text .. postTag +-- end + + if self.gender or self.culture then + ssml=string.format("%s", gender, language, Text) + end end env.info("FF freq") @@ -1406,9 +1382,10 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab self:T("GRPC.tts") self:T(ssml) self:T(freq) - self:T({options}) + self:T(options) UTILS.PrintTableToLog(options) - --GRPC.tts(ssml, freq*1e6, options) + env.info(UTILS.OneLineSerialize(options)) + GRPC.tts(ssml, freq*1e6, options) end end @@ -1568,345 +1545,6 @@ function MSRS:LoadConfigFile(Path,Filename) return true end -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- MSRS DCS-gRPC alternate backend -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Alternate backend for MSRS to enable text-to-speech via DCS-gRPC. --- ### Author: **dogjutsu** --- A table containing functions and variables for MSRS to use DCS-gRPC [DCS-gRPC](https://github.com/DCS-gRPC/rust-server) 0.7.0 or newer as a backend to transmit over SRS. --- This is not a standalone class. Instead, variables and functions under the `Vars` and `Functions` tables get added to or replace MSRS variables/functions when activated. --- --- @type MSRS_BACKEND_DCSGRPC --- @field #number version Version number of this alternate backend. --- @field #table Functions A table of functions that will add or replace the default MSRS class functions. --- @field #table Vars A table of variables that will add or replace the default MSRS class variables. -MSRS_BACKEND_DCSGRPC = {} -MSRS_BACKEND_DCSGRPC.version = 0.1 - -MSRS_BACKEND_DCSGRPC.Functions = {} -MSRS_BACKEND_DCSGRPC.Vars = { provider = 'win' } - ---- Called by @{#MSRS._NewAltBackend} (if present) immediately after an alternate backend functions and variables for MSRS are added/replaced. --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions._MSRSbackendInit = function (self) - BASE:I('Loaded MSRS DCS-gRPC alternate backend version ' .. self.AltBackend.version or 'unspecified') - - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- MSRS DCS-gRPC alternate backend User Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- No-op replacement function for @{#MSRS.SetPath} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetPath = function (self) - return self -end - ---- No-op replacement function for @{#MSRS.GetPath} (Not Applicable) --- @param #MSRS self --- @return #string Empty string -MSRS_BACKEND_DCSGRPC.Functions.GetPath = function (self) - return '' -end - ---- No-op replacement function for @{#MSRS.SetVolume} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetVolume = function (self) - BASE:I('NOTE: MSRS:SetVolume() not used with DCS-gRPC backend.') - return self -end - ---- No-op replacement function for @{#MSRS.GetVolume} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.GetVolume = function (self) - BASE:I('NOTE: MSRS:GetVolume() not used with DCS-gRPC backend.') - return 1 -end - ---- No-op replacement function for @{#MSRS.SetGender} (Not Applicable) --- @param #MSRS self --- #string Gender Gender: "male" or "female" --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetGender = function (self, Gender) - -- Use DCS-gRPC default if not specified - - if Gender then - self.gender=Gender:lower() - end - - -- Debug output. - self:T("Setting gender to "..tostring(self.gender)) - return self -end - ---- Replacement function for @{#MSRS.SetGoogle} to use google text-to-speech. (API key set as part of DCS-gRPC configuration) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetGoogle = function (self) - self.provider = 'gcloud' - return self -end - ---- Replacement function for @{#MSRS.SetGoogle} to use google text-to-speech - here: Set the API key --- @param #MSRS self --- @param #string key --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetAPIKey = function (self, key) - self.APIKey = key - return self -end - ---- Replacement function for @{#MSRS.SetGoogle} to use google text-to-speech - here: Set the API key --- @param #MSRS self --- @param #string voice --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetDefaultVoice = function (self, voice) - self.defaultVoice = voice - return self -end - ---- MSRS:SetAWS() Use AWS text-to-speech. (API key set as part of DCS-gRPC configuration) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetAWS = function (self) - self.provider = 'aws' - return self -end - ---- MSRS:SetAzure() Use Azure text-to-speech. (API key set as part of DCS-gRPC configuration) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetAzure = function (self) - self.provider = 'azure' - return self -end - ---- MSRS:SetWin() Use local Windows OS text-to-speech (Windows Server 2019 / Windows 11 / Windows 10? or newer). (Default) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetWin = function (self) - self.provider = 'win' - return self -end - ---- Replacement function for @{#MSRS.Help} to display help. --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.Help = function (self) - env.info('For DCS-gRPC help, please see: https://github.com/DCS-gRPC/rust-server') - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- MSRS DCS-gRPC alternate backend Transmission Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- No-op replacement function for @{#MSRS.PlaySoundFile} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlaySoundFile = function (self) - BASE:E("ERROR: MSRS:PlaySoundFile() is not supported by the DCS-gRPC backend.") - return self -end - ---- Replacement function for @{#MSRS.PlaySoundText} --- @param #MSRS self --- @param Sound.SoundOutput#SOUNDTEXT SoundText Sound text. --- @param #number Delay Delay in seconds, before the sound file is played. --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlaySoundText = function (self, SoundText, Delay) - - if Delay and Delay>0 then - self:ScheduleOnce(Delay, self.PlaySoundText, self, SoundText, 0) - else - self:_DCSgRPCtts(tostring(SoundText.text)) - end - - return self -end - ---- Replacement function for @{#MSRS.PlayText} --- @param #MSRS self --- @param #string Text Text message. --- @param #number Delay Delay in seconds, before the message is played. --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlayText = function (self, Text, Delay) - - if Delay and Delay>0 then - self:ScheduleOnce(Delay, self.PlayText, self, Text, 0) - else - self:_DCSgRPCtts(tostring(Text)) - end - - return self -end - ---- Replacement function for @{#MSRS.PlayText} --- @param #MSRS self --- @param #string Text Text message. --- @param #number Delay Delay in seconds, before the message is played. --- @param #table Frequencies Radio frequencies. --- @param #table Modulations Radio modulations. (Non-functional, DCS-gRPC sets automatically) --- @param #string Gender Gender. (Non-functional, only 'Voice' supported) --- @param #string Culture Culture. (Non-functional, only 'Voice' supported) --- @param #string Voice Voice. --- @param #number Volume Volume. (Non-functional, all transmissions full volume with DCS-gRPC) --- @param #string Label Label. --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlayTextExt = function (self, Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label) - if Delay and Delay>0 then - self:ScheduleOnce(Delay, self.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label) - else - self:_DCSgRPCtts(tostring(Text), nil, Frequencies, Voice, Label) - end - - return self -end - ---- No-op replacement function for @{#MSRS.PlayTextFile} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlayTextFile = function (self, TextFile, Delay) - BASE:E("ERROR: MSRS:PlayTextFile() is not supported by the DCS-gRPC backend.") - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- MSRS DCS-gRPC alternate backend Misc Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- DCS-gRPC v0.70 TTS API call: --- GRPC.tts(ssml, frequency[, options]) - Synthesize text (ssml; SSML tags supported) to speech and transmit it over SRS on the frequency with the following optional options (and their defaults): - --- { --- -- The plain text without any transformations made to it for the purpose of getting it spoken out --- -- as desired (no SSML tags, no FOUR NINER instead of 49, ...). Even though this field is --- -- optional, please consider providing it as it can be used to display the spoken text to players --- -- with hearing impairments. --- plaintext = null, -- e.g. `= "Hello Pilot"` - --- -- Name of the SRS client. --- srsClientName = "DCS-gRPC", - --- -- The origin of the transmission. Relevant if the SRS server has "Line of --- -- Sight" and/or "Distance Limit" enabled. --- position = { --- lat = 0.0, --- lon = 0.0, --- alt = 0.0, -- in meters --- }, - --- -- The coalition of the transmission. Relevant if the SRS server has "Secure --- -- Coalition Radios" enabled. Supported values are: `blue` and `red`. Defaults --- -- to being spectator if not specified. --- coalition = null, - --- -- TTS provider to be use. Defaults to the one configured in your config or to Windows' --- -- built-in TTS. Examples: --- -- `= { aws = {} }` / `= { aws = { voice = "..." } }` enable AWS TTS --- -- `= { azure = {} }` / `= { azure = { voice = "..." } }` enable Azure TTS --- -- `= { gcloud = {} }` / `= { gcloud = { voice = "..." } }` enable Google Cloud TTS --- -- `= { win = {} }` / `= { win = { voice = "..." } }` enable Windows TTS --- provider = null, --- } - ---- Make DCS-gRPC API call to transmit text-to-speech over SRS. --- @param #MSRS self --- @param #string Text Text of message to transmit (can also be SSML). --- @param #string Optional plaintext version of message (for accessiblity) --- @param #table Frequencies Radio frequencies to transmit on. Can also accept a number in MHz. --- @param #string Voice Voice for the TTS provider to user. --- @param #string Label Label (SRS diplays as name of the transmitter). --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Frequencies, Voice, Label) - - BASE:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") - BASE:T({Text, Plaintext, Frequencies, Voice, Label}) - - local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions - local ssml = Text or '' - - local XmitFrequencies = Frequencies or self.Frequency - if type(XmitFrequencies)~="table" then - XmitFrequencies={XmitFrequencies} - end - - options.plaintext = Plaintext - options.srsClientName = Label or self.Label - options.position = {} - if self.coordinate then - options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) - end - - options.position.lat = options.position.lat or 0.0 - options.position.lon = options.position.lon or 0.0 - options.position.alt = options.position.alt or 0.0 - - if UTILS.GetCoalitionName(self.coalition) == 'Blue' then - options.coalition = 'blue' - elseif UTILS.GetCoalitionName(self.coalition) == 'Red' then - options.coalition = 'red' - end - - local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider - - options.provider = {} - - options.provider[provider] = {} - - if self.APIKey then - options.provider[provider].key = self.APIKey - end - - if self.defaultVoice then - options.provider[provider].defaultVoice = self.defaultVoice - end - - if self.voice then - - -- Set voice. - options.provider[provider].voice = Voice or self.voice or self.defaultVoice - - elseif ssml then - - -- DCS-gRPC doesn't directly support language/gender, but can use SSML - -- Only use if a voice isn't explicitly set - - local preTag, genderProp, langProp, postTag = '', '', '', '' - - if self.gender then - genderProp = ' gender=\"' .. self.gender .. '\"' - end - if self.culture then - langProp = ' language=\"' .. self.culture .. '\"' - end - - if self.culture or self.gender then - preTag = '' - postTag = '' - ssml = preTag .. Text .. postTag - end - - end - - for _,_freq in ipairs(XmitFrequencies) do - local freq = _freq*1000000 - BASE:T("GRPC.tts") - BASE:T(ssml) - BASE:T(freq) - BASE:T({options}) - GRPC.tts(ssml, freq, options) - end - -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Manages radio transmissions. From 97161627397dacf24bd16bb7cc2334080719c8c8 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 17 Dec 2023 22:55:31 +0100 Subject: [PATCH 05/10] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 64 +++++++++++---------------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index c85ddcd9b..a7f810493 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -48,10 +48,11 @@ -- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". -- @field #string Label Label showing up on the SRS radio overlay. Default is "ROBOT". No spaces allowed. -- @field #table AltBackend Table containing functions and variables to enable an alternate backend to transmit to SRS. --- @field #string ConfigFileName Name of the standard config file --- @field #string ConfigFilePath Path to the standard config file --- @field #boolean ConfigLoaded --- @field #string ttsprovider Default provider TTS backend, e.g. "Google" or "Microsoft", default is Microsoft +-- @field #string ConfigFileName Name of the standard config file. +-- @field #string ConfigFilePath Path to the standard config file. +-- @field #boolean ConfigLoaded If `true` if config file was loaded. +-- @field #string ttsprovider Default provider TTS backend, e.g. "Google" or "Microsoft", default is Microsoft. +-- @field #table poptions Provider options. Each element is a data structure of type `MSRS.ProvierOptions`. -- @extends Core.Base#BASE --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -64,8 +65,9 @@ -- -- ## Prerequisites -- --- This script needs SRS version >= 1.9.6. --- Optional: DCS-gRPC as backend to communicate with SRS (vide infra) +-- * This script needs SRS version >= 1.9.6 +-- * You need to de-sanitize os, io and lfs in hte missionscripting.lua +-- * Optional: DCS-gRPC as backend to communicate with SRS (vide infra) -- -- ## Knwon Issues -- @@ -112,7 +114,7 @@ -- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml -- -- --- **Pro-Tipp** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing. +-- **Pro-Tip** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing. -- and also the Google Console error, in case you have missed a step in setting up your Google TTS. -- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` -- Plays a message on 255 MHz AM for the blue coalition in-game. @@ -210,7 +212,7 @@ MSRS = { ConfigFileName = "Moose_MSRS.lua", ConfigFilePath = "Config\\", ConfigLoaded = false, - poptions = {}, + poptions = {}, } --- MSRS class version. @@ -343,6 +345,8 @@ MSRS.Provider = { --- Provider options. -- @type MSRS.ProviderOptions +-- @field #string provider Provider. +-- @field #string credentials Google credentials JSON file (full path). -- @field #string key Access key (DCS-gRPC with Google, ASW, AZURE as provider). -- @field #string secret Secret key (DCS-gRPC with ASW as provider) -- @field #string region Region. @@ -653,7 +657,7 @@ end --- Get modulations. -- @param #MSRS self --- @param #table Modulations. +-- @return #table Modulations. function MSRS:GetModulations() return self.modulations end @@ -874,32 +878,6 @@ function MSRS:Help() return self end ---- Sets an alternate SRS backend to be used by MSRS to transmit over SRS for all new MSRS class instances. --- @param #table Backend A table containing a table `Functions` with new/replacement class functions and `Vars` with new/replacement variables. --- @return #boolean Returns 'true' on success. -function MSRS.SetDefaultBackend(Backend) - if type(Backend) == "table" then - MSRS.AltBackend = UTILS.DeepCopy(Backend) - else - return false - end - - return true -end - ---- Restores default SRS backend (DCS-SR-ExternalAudio.exe) to be used by all new MSRS class instances to transmit over SRS. --- @return #boolean Returns 'true' on success. -function MSRS.ResetDefaultBackend() - MSRS.AltBackend = nil - return true -end - ---- Sets DCS-gRPC as the default SRS backend for all new MSRS class instances. --- @return #boolean Returns 'true' on success. -function MSRS.SetDefaultBackendGRPC() - return MSRS.SetDefaultBackend(MSRS_BACKEND_DCSGRPC) -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Transmission Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -984,7 +962,7 @@ function MSRS:PlayText(Text, Delay, Coordinate) else - self:PlayTextExt(Text,Delay, nil, nil, nil, nil, nil, nil, nil,Coordinate) + self:PlayTextExt(Text, Delay, nil, nil, nil, nil, nil, nil, nil, Coordinate) end @@ -1012,6 +990,9 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) else + Frequencies = Frequencies or self:GetFrequencies() + Modulations = Modulations or self:GetModulations() + if self.backend==MSRS.Backend.SRSEXE then -- Get command line. @@ -1162,9 +1143,14 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt) end - -- Set google. - if self.google and self.ttsprovider == "Google" then - command=command..string.format(' --ssml -G "%s"', self.google) + -- Set provider options + if self.provider==MSRS.Provider.GOOGLE then + local pops=self:GetProviderOptions() + command=command..string.format(' --ssml -G "%s"', pops.credentials) + elseif self.provider==MSRS.Provider.WINDOWS then + -- Nothing to do. + else + self:E("ERROR: SRS only supports WINWOWS and GOOGLE as TTS providers! Use DCS-gRPC backend for other providers such as ") end -- Debug output. From c72f10955362b10b425ebbd281bc9a009b974c5c Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 19 Dec 2023 00:02:17 +0100 Subject: [PATCH 06/10] SRS --- Moose Development/Moose/Core/Point.lua | 13 +- Moose Development/Moose/Core/Zone.lua | 18 ++ Moose Development/Moose/Sound/SRS.lua | 240 ++++++++++++++++--------- 3 files changed, 179 insertions(+), 92 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index a3c83da1f..89807b436 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2456,15 +2456,18 @@ do -- COORDINATE -- Write command as string and execute that. Idea by Grimes https://forum.dcs.world/topic/324201-mark-to-all-function/#comment-5273793 local s=string.format("trigger.action.markupToAll(7, %d, %d,", Coalition, MarkID) for _,vec in pairs(vecs) do - s=s..string.format("%s,", UTILS._OneLineSerialize(vec)) + --s=s..string.format("%s,", UTILS._OneLineSerialize(vec)) + s=s..string.format("{x=%.1f, y=%.1f, z=%.1f},", vec.x, vec.y, vec.z) end - s=s..string.format("%s, %s, %s, %s", UTILS._OneLineSerialize(Color), UTILS._OneLineSerialize(FillColor), tostring(LineType), tostring(ReadOnly)) - if Text and Text~="" then - s=s..string.format(", \"%s\"", Text) + s=s..string.format("{%.3f, %.3f, %.3f, %.3f},", Color[1], Color[2], Color[3], Color[4]) + s=s..string.format("{%.3f, %.3f, %.3f, %.3f},", FillColor[1], FillColor[2], FillColor[3], FillColor[4]) + s=s..string.format("%d,", LineType or 1) + s=s..string.format("%s", tostring(ReadOnly)) + if Text and type(Text)=="string" and string.len(Text)>0 then + s=s..string.format(", \"%s\"", tostring(Text)) end s=s..")" - -- Execute string command local success=UTILS.DoString(s) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 09ef67c2f..1037ddf02 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2473,6 +2473,20 @@ end -- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. -- @return #ZONE_POLYGON_BASE self function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, IncludeTriangles) + + local coords = self:GetVerticiesCoordinates() + + local coord=coords[1] --Core.Point#COORDINATE + + table.remove(coords, 1) + + coord:MarkupToAllFreeForm(coords, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, "Drew Polygon") + + if true then + return + end + + if self._.Polygon and #self._.Polygon >= 3 then Coalition = Coalition or self:GetDrawCoalition() @@ -3610,13 +3624,17 @@ ZONE_OVAL = { --- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle. --- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #string name Name of the zone. -- @param #table vec2 The center point of the oval -- @param #number major_axis The major axis of the oval -- @param #number minor_axis The minor axis of the oval -- @param #number angle The angle of the oval -- @return #ZONE_OVAL The new oval function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle) + self = BASE:Inherit(self, ZONE_BASE:New()) + self.ZoneName = name self.CenterVec2 = vec2 self.MajorAxis = major_axis diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index a7f810493..82689f9e5 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -42,7 +42,7 @@ -- @field #number volume Volume between 0 (min) and 1 (max). Default 1. -- @field #string culture Culture. Default "en-GB". -- @field #string gender Gender. Default "female". --- @field #string voice Specific voce. +-- @field #string voice Specific voice. -- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send. -- @field #string path Path to the SRS exe. This includes the final slash "/". -- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". @@ -365,17 +365,6 @@ MSRS.Provider = { -- @field #MSRS.ProviderOptions aws -- @field #string DefaultProvider -MSRS.GRPCOptions = {} -- #MSRS.GRPCOptions -MSRS.GRPCOptions.gcloud = {} -- #MSRS.ProviderOptions -MSRS.GRPCOptions.win = {} -- #MSRS.ProviderOptions -MSRS.GRPCOptions.azure = {} -- #MSRS.ProviderOptions -MSRS.GRPCOptions.aws = {} -- #MSRS.ProviderOptions - -MSRS.GRPCOptions.win.defaultVoice = "Hedda" -MSRS.GRPCOptions.win.voice = "Hedda" - -MSRS.GRPCOptions.DefaultProvider = "win" - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -700,20 +689,36 @@ function MSRS:SetVoice(Voice) return self end ---- Set to use a specific voice. Will override gender and culture settings. +--- Set to use a specific voice for a given provider. Note that this will override any gender and culture settings. -- @param #MSRS self +-- @param #string Provider Provider. Default `MSRS.Provider.WINDOWS`. -- @param #string Voice Voice. -- @return #MSRS self -function MSRS:SetDefaultVoice(Voice) - - self.defaultVoice=Voice +function MSRS:SetProviderVoice(Provider, Voice) - local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider or "win" - self.GRPCOptions[provider].defaultVoice = Voice + self.poptions=self.poptions or {} + + self.poptions[Provider or MSRS.Provider.WINDOWSo]=Voice return self end +--- Get voice. +-- @param #MSRS self +-- @param #string Provider Provider. Default is the currently set provider (`self.provider`). +-- @return #MSRS self +function MSRS:GetVoice(Provider) + + Provider=Provider or self.provider + + if Provider then + return self.poptions[Provider].voice + else + return self.voice + end + +end + --- Set the coordinate from which the transmissions will be broadcasted. Note that this is only a factor if SRS has line-of-sight or distance enabled. -- @param #MSRS self -- @param Core.Point#COORDINATE Coordinate Origin of the transmission. @@ -796,6 +801,34 @@ end -- @param #string Region Region to use. -- @return #MSRS.ProviderOptions Provider optionas table. function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) + + local option=MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) + + if self then + + self.poptions=self.poptions or {} + + self.poptions[Provider]=option + + else + + MSRS.poptions=MSRS.poptions or {} + + MSRS.poptions[Provider]=option + + end + + return option +end + +--- Create MSRS.ProviderOptions. +-- @param #string Provider Provider. +-- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. +-- @param #string AccessKey Your API access key. +-- @param #string SecretKey Your secret key. +-- @param #string Region Region to use. +-- @return #MSRS.ProviderOptions Provider optionas table. +function MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) local option={} --#MSRS.ProviderOptions @@ -804,10 +837,6 @@ function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey option.key=AccessKey option.secret=SecretKey option.region=Region - - self.poptions=self.poptions or {} - - self.poptions[Provider]=option return option end @@ -824,7 +853,7 @@ function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) return self end ---- Set provider options and credentials for Google Cloud. +--- Get provider options. -- @param #MSRS self -- @param #string Provider Provider. Default is as set via @{#MSRS.SetProvider}. -- @return #MSRS.ProviderOptions Provider options. @@ -1107,7 +1136,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp coal=coal or self.coalition gender=gender or self.gender - voice=voice or self.voice + voice=voice or self:GetVoice(self.provider) or self.voice culture=culture or self.culture volume=volume or self.volume speed=speed or self.speed @@ -1297,9 +1326,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab self:I("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") self:I({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) - - --local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions - + local options = {} -- #MSRS.GRPCOptions local ssml = Text or '' @@ -1323,14 +1350,14 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab options.coalition = UTILS.GetCoalitionName(self.coalition):lower() -- Provider (win, gcloud, ...) - local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider + local provider = self.provider or MSRS.Provider.WINDOWS -- Provider options: voice, credentials options.provider = {} options.provider[provider] = self:GetProviderOptions(provider) -- Voice - Voice=Voice or self.voice + Voice=Voice or self:GetVoice(self.provider) or self.voice if Voice then -- We use a specific voice @@ -1453,76 +1480,115 @@ end function MSRS:LoadConfigFile(Path,Filename) if lfs == nil then - env.info("*****Note - lfs and os need to be desanitized for MSRS to work!") - return false + env.info("*****Note - lfs and os need to be desanitized for MSRS to work!") + return false end + local path = Path or lfs.writedir()..MSRS.ConfigFilePath local file = Filename or MSRS.ConfigFileName or "Moose_MSRS.lua" local pathandfile = path..file local filexsists = UTILS.FileExists(pathandfile) if filexsists and not MSRS.ConfigLoaded then + + env.info("FF reading config file") + + -- Load global MSRS_Config assert(loadfile(path..file))() - -- now we should have a global var MSRS_Config + if MSRS_Config then - if self then - self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - self.port = MSRS_Config.Port or 5002 - self.frequencies = MSRS_Config.Frequency or {127,243} - self.modulations = MSRS_Config.Modulation or {0,0} - self.coalition = MSRS_Config.Coalition or 0 - if MSRS_Config.Coordinate then - self.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1],MSRS_Config.Coordinate[2],MSRS_Config.Coordinate[3]) - end - self.culture = MSRS_Config.Culture or "en-GB" - self.gender = MSRS_Config.Gender or "male" - self.google = MSRS_Config.Google - if MSRS_Config.Provider then - self.ttsprovider = MSRS_Config.Provider - end - self.Label = MSRS_Config.Label or "MSRS" - self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel - if MSRS_Config.GRPC then - self.provider = MSRS_Config.GRPC.DefaultProvider - if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then - self.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key - self.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice - self.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret - self.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region - end - end - self.ConfigLoaded = true - else - MSRS.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - MSRS.port = MSRS_Config.Port or 5002 - MSRS.frequencies = MSRS_Config.Frequency or {127,243} - MSRS.modulations = MSRS_Config.Modulation or {0,0} - MSRS.coalition = MSRS_Config.Coalition or 0 - if MSRS_Config.Coordinate then - MSRS.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1],MSRS_Config.Coordinate[2],MSRS_Config.Coordinate[3]) - end - MSRS.culture = MSRS_Config.Culture or "en-GB" - MSRS.gender = MSRS_Config.Gender or "male" - MSRS.google = MSRS_Config.Google - if MSRS_Config.Provider then - MSRS.ttsprovider = MSRS_Config.Provider - end - MSRS.Label = MSRS_Config.Label or "MSRS" - MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel - if MSRS_Config.GRPC then - MSRS.provider = MSRS_Config.GRPC.DefaultProvider - if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then - MSRS.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key - MSRS.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice - MSRS.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret - MSRS.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region - end - end - MSRS.ConfigLoaded = true + + local Self = self or MSRS --#MSRS + + Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + Self.port = MSRS_Config.Port or 5002 + Self.frequencies = MSRS_Config.Frequency or {127,243} + Self.modulations = MSRS_Config.Modulation or {0,0} + Self.coalition = MSRS_Config.Coalition or 0 + if MSRS_Config.Coordinate then + Self.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) end + Self.culture = MSRS_Config.Culture or "en-GB" + Self.gender = MSRS_Config.Gender or "male" + Self.Label = MSRS_Config.Label or "MSRS" + Self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel + + Self.provider = MSRS_Config.Provider or MSRS.Provider.WINDOWS + for _,provider in pairs(MSRS.Provider) do + if MSRS_Config[provider] then + Self.poptions[provider]=MSRS_Config[provider] + end + end + + Self.ConfigLoaded = true + + if false then + + if self then + self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + self.port = MSRS_Config.Port or 5002 + self.frequencies = MSRS_Config.Frequency or {127,243} + self.modulations = MSRS_Config.Modulation or {0,0} + self.coalition = MSRS_Config.Coalition or 0 + if MSRS_Config.Coordinate then + self.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) + end + self.culture = MSRS_Config.Culture or "en-GB" + self.gender = MSRS_Config.Gender or "male" + self.google = MSRS_Config.Google + if MSRS_Config.Provider then + self.ttsprovider = MSRS_Config.Provider + end + self.Label = MSRS_Config.Label or "MSRS" + self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel + + if MSRS_Config.GRPC then + self.provider = MSRS_Config.GRPC.DefaultProvider + if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then + self.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key + self.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice + self.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret + self.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region + end + end + + self.ConfigLoaded = true + else + + MSRS.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + MSRS.port = MSRS_Config.Port or 5002 + MSRS.frequencies = MSRS_Config.Frequency or {127,243} + MSRS.modulations = MSRS_Config.Modulation or {0,0} + MSRS.coalition = MSRS_Config.Coalition or 0 + if MSRS_Config.Coordinate then + MSRS.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) + end + MSRS.culture = MSRS_Config.Culture or "en-GB" + MSRS.gender = MSRS_Config.Gender or "male" + MSRS.google = MSRS_Config.Google + if MSRS_Config.Provider then + MSRS.ttsprovider = MSRS_Config.Provider + end + MSRS.Label = MSRS_Config.Label or "MSRS" + MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel + if MSRS_Config.GRPC then + MSRS.provider = MSRS_Config.GRPC.DefaultProvider + if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then + MSRS.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key + MSRS.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice + MSRS.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret + MSRS.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region + end + end + MSRS.ConfigLoaded = true + end + + end + end env.info("MSRS - Successfully loaded default configuration from disk!",false) end + if not filexsists then env.info("MSRS - Cannot find default configuration file!",false) return false @@ -1921,7 +1987,7 @@ function MSRSQUEUE:_CheckRadioQueue(delay) end ---MSRS.LoadConfigFile() +MSRS.LoadConfigFile() ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 0d18ce086c7a2d31d5f919fb2ec10a0cd7668e89 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 21 Dec 2023 22:33:08 +0100 Subject: [PATCH 07/10] Update Zone.lua --- Moose Development/Moose/Core/Zone.lua | 68 --------------------------- 1 file changed, 68 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 2b5b1d4f3..d9abec36e 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2509,74 +2509,6 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph end - if self._.Polygon and #self._.Polygon >= 3 then - Coalition = Coalition or self:GetDrawCoalition() - - -- Set draw coalition. - self:SetDrawCoalition(Coalition) - - Color = Color or self:GetColorRGB() - Alpha = Alpha or 1 - - -- Set color. - self:SetColor(Color, Alpha) - - FillColor = FillColor or self:GetFillColorRGB() - if not FillColor then - UTILS.DeepCopy(Color) - end - FillAlpha = FillAlpha or self:GetFillColorAlpha() - if not FillAlpha then - FillAlpha = 0.15 - end - - -- Set fill color -----------> has fill color worked in recent versions of DCS? - -- doing something like - -- - -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") - -- - -- doesn't seem to fill in the shape for an n-sided polygon - self:SetFillColor(FillColor, FillAlpha) - - IncludeTriangles = IncludeTriangles or false - - -- just draw the triangles, we get the outline for free - if IncludeTriangles then - for _, triangle in pairs(self._Triangles) do - local draw_ids = triangle:Draw() - table.combine(self.DrawID, draw_ids) - end - -- draw outline only - else - local coords = self:GetVerticiesCoordinates() - for i = 1, #coords do - local c1 = coords[i] - local c2 = coords[i % #coords + 1] - table.add(self.DrawID, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly)) - end - end - - if self._.Polygon and #self._.Polygon >= 3 then - Coalition = Coalition or self:GetDrawCoalition() - - -- Set draw coalition. - self:SetDrawCoalition(Coalition) - - Color = Color or self:GetColorRGB() - Alpha = Alpha or self:GetColorAlpha() - - FillColor = FillColor or self:GetFillColorRGB() - FillAlpha = FillAlpha or self:GetFillColorAlpha() - - if FillColor then - self:ReFill(FillColor,FillAlpha) - end - - if Color then - self:ReDrawBorderline(Color,Alpha,LineType) - end - end - return self end From e89b921f3e64aae1235fb8c83c47b0f8983275dc Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 22 Dec 2023 10:27:00 +0100 Subject: [PATCH 08/10] Update Zone.lua --- Moose Development/Moose/Core/Zone.lua | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index d9abec36e..3317df260 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2496,6 +2496,30 @@ end -- @return #ZONE_POLYGON_BASE self function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, IncludeTriangles) + + if self._.Polygon and #self._.Polygon >= 3 then + Coalition = Coalition or self:GetDrawCoalition() + + -- Set draw coalition. + self:SetDrawCoalition(Coalition) + + Color = Color or self:GetColorRGB() + Alpha = Alpha or self:GetColorAlpha() + + FillColor = FillColor or self:GetFillColorRGB() + FillAlpha = FillAlpha or self:GetFillColorAlpha() + + if FillColor then + self:ReFill(FillColor,FillAlpha) + end + + if Color then + self:ReDrawBorderline(Color,Alpha,LineType) + end + end + + + if false then local coords = self:GetVerticiesCoordinates() local coord=coords[1] --Core.Point#COORDINATE @@ -2507,7 +2531,7 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph if true then return end - + end return self end From 9ec92a8fcaaa617039fb0ccd18437be75d5e57f4 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 23 Dec 2023 15:57:27 +0100 Subject: [PATCH 09/10] SRS - Refactoring --- Moose Development/Moose/Ops/ATIS.lua | 6 +- Moose Development/Moose/Sound/SRS.lua | 471 +++++++++++++++----------- 2 files changed, 279 insertions(+), 198 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 6819ee36c..064ab1880 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -890,7 +890,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.10.4" +ATIS.version = "1.0.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1528,12 +1528,12 @@ end --- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary. -- @param #ATIS self --- @param #string PathToSRS Path to SRS directory. +-- @param #string PathToSRS Path to SRS directory (only necessary if SRS exe backend is used). -- @param #string Gender Gender: "male" or "female" (default). -- @param #string Culture Culture, e.g. "en-GB" (default). -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. -- @param #number Port SRS port. Default 5002. --- @param #string GoogleKey Path to Google JSON-Key. +-- @param #string GoogleKey Path to Google JSON-Key (SRS exe backend) or Google API key (DCS-gRPC backend). -- @return #ATIS self function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) if PathToSRS or MSRS.path then diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 82689f9e5..899d41fba 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -42,17 +42,16 @@ -- @field #number volume Volume between 0 (min) and 1 (max). Default 1. -- @field #string culture Culture. Default "en-GB". -- @field #string gender Gender. Default "female". --- @field #string voice Specific voice. +-- @field #string voice Specific voice. Only used if no explicit provider voice specified. -- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send. --- @field #string path Path to the SRS exe. This includes the final slash "/". --- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". +-- @field #string path Path to the SRS exe. -- @field #string Label Label showing up on the SRS radio overlay. Default is "ROBOT". No spaces allowed. --- @field #table AltBackend Table containing functions and variables to enable an alternate backend to transmit to SRS. -- @field #string ConfigFileName Name of the standard config file. -- @field #string ConfigFilePath Path to the standard config file. -- @field #boolean ConfigLoaded If `true` if config file was loaded. --- @field #string ttsprovider Default provider TTS backend, e.g. "Google" or "Microsoft", default is Microsoft. -- @field #table poptions Provider options. Each element is a data structure of type `MSRS.ProvierOptions`. +-- @field #string provider Provider of TTS (win, gcloud, azure, amazon). +-- @field #string backend Backend used as interface to SRS (MSRS.Backend.SRSEXE or MSRS.Backend.GRPC). -- @extends Core.Base#BASE --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -106,29 +105,18 @@ -- -- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`. -- --- ## Set Google --- --- Use Google's text-to-speech engine with the @{#MSRS.SetGoogle} function, e.g. ':SetGoogle()'. --- By enabling this it also allows you to utilize SSML in your text for added flexibility. --- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech --- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml --- --- --- **Pro-Tip** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing. --- and also the Google Console error, in case you have missed a step in setting up your Google TTS. --- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` --- Plays a message on 255 MHz AM for the blue coalition in-game. --- -- ## Set Voice -- -- Use a specific voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. --- If enabling SetGoogle(), you can use voices provided by Google --- Google's supported voices: https://cloud.google.com/text-to-speech/docs/voices +-- +-- Note that you can set voices for each provider via the @{#MSRS.SetVoiceProvider} function. Also shortcuts are available, *i.e.* +-- @{#MSRS.SetVoiceWindows}, @{#MSRS.SetVoiceGoogle}, @{#MSRS.SetVoiceAzure} and @{#MSRS.SetVoiceAmazon}. +-- -- For voices there are enumerators in this class to help you out on voice names: -- --- MSRS.Voices.Microsoft -- e.g. MSRS.Voices.Microsoft.Hedda - the Microsoft enumerator contains all voices known to work with SRS --- MSRS.Voices.Google -- e.g. MSRS.Voices.Google.Standard.en_AU_Standard_A or MSRS.Voices.Google.Wavenet.de_DE_Wavenet_C - The Google enumerator contains voices for EN, DE, IT, FR and ES. +-- MSRS.Voices.Microsoft -- e.g. MSRS.Voices.Microsoft.Hedda - the Microsoft enumerator contains all voices known to work with SRS +-- MSRS.Voices.Google -- e.g. MSRS.Voices.Google.Standard.en_AU_Standard_A or MSRS.Voices.Google.Wavenet.de_DE_Wavenet_C - The Google enumerator contains voices for EN, DE, IT, FR and ES. -- -- ## Set Coordinate -- @@ -146,8 +134,63 @@ -- ## Config file for many variables, auto-loaded by Moose -- -- See @{#MSRS.LoadConfigFile} for details on how to set this up. +-- +-- ## TTS Providers +-- +-- The default provider for generating speech from text is the native Windows TTS service. Note that you need to install the voices you want to use. +-- +-- **Pro-Tip** - use the command line with power shell to call `DCS-SR-ExternalAudio.exe` - it will tell you what is missing, +-- and also the Google Console error, in case you have missed a step in setting up your Google TTS. +-- For example, `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` +-- plays a message on 255 MHz AM for the blue coalition in-game. +-- +-- ### Google +-- +-- In order to use Google Cloud for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsGoogle} functions: +-- +-- msrs:SetProvider(MSRS.Provider.GOOGLE) +-- msrs:SetProviderOptionsGoogle(CredentialsFile, AccessKey) +-- +-- The parameter `CredentialsFile` is used with the default 'DCS-SR-ExternalAudio.exe' backend and must be the full path to the credentials JSON file. +-- The `AccessKey` parameter is used with the DCS-gRPC backend (see below). +-- +-- You can set the voice to use with Google via @{#MSRS.SetVoiceGoogle}. +-- +-- When using Google it also allows you to utilize SSML in your text for more flexibility. +-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech +-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml +-- +-- ### Amazon Web Service [Only DCS-gRPC backend] +-- +-- In order to use Amazon Web Service (ASW) for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsAmazon} functions: +-- +-- msrs:SetProvider(MSRS.Provider.AMAZON) +-- msrs:SetProviderOptionsAmazon(AccessKey, SecretKey, Region) +-- +-- The parameters `AccessKey` and `SecretKey` are your ASW access and secret keys, respectively. The parameter `Region` is your [ASW region](https://docs.aws.amazon.com/general/latest/gr/pol.html). +-- +-- You can set the voice to use with ASW via @{#MSRS.SetVoiceAmazon}. +-- +-- ### Microsoft Azure [Only DCS-gRPC backend] +-- +-- In order to use Microsoft Azure for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsAzure} functions: +-- +-- msrs:SetProvider(MSRS.Provider.AZURE) +-- msrs:SetProviderOptionsAmazon(AccessKey, Region) +-- +-- The parameter `AccessKey` is your Azure access key. The parameter `Region` is your [Azure region](https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). +-- +-- You can set the voice to use with Azure via @{#MSRS.SetVoiceAzure}. +-- +-- ## Backend +-- +-- The default interface to SRS is via calling the 'DCS-SR-ExternalAudio.exe'. As noted above, this has the unavoidable drawback that a pop-up briefly appears +-- and DCS might be put out of focus. +-- +-- ## DCS-gRPC as an alternative to 'DCS-SR-ExternalAudio.exe' for TTS -- --- ## Set DCS-gRPC as an alternative to 'DCS-SR-ExternalAudio.exe' for TTS +-- Another interface to SRS is [DCS-gRPC](https://github.com/DCS-gRPC/rust-server). This does not call an exe file and therefore avoids the annoying pop-up window. +-- In addition to Windows and Google cloud, it also offers Microsoft Azure and Amazon Web Service as providers for TTS. -- -- Use @{#MSRS.SetDefaultBackendGRPC} to enable [DCS-gRPC](https://github.com/DCS-gRPC/rust-server) as an alternate backend for transmitting text-to-speech over SRS. -- This can be useful if 'DCS-SR-ExternalAudio.exe' cannot be used in the environment, or to use Azure or AWS clouds for TTS. Note that DCS-gRPC does not (yet?) support @@ -217,7 +260,7 @@ MSRS = { --- MSRS class version. -- @field #string version -MSRS.version="0.2.0" +MSRS.version="0.3.0" --- Voices -- @type MSRS.Voices @@ -323,7 +366,7 @@ MSRS.Voices = { --- Backend options to communicate with SRS. -- @type MSRS.Backend --- @field #string SRSEXE Use SRS exe. +-- @field #string SRSEXE Use `DCS-SR-ExternalAudio.exe`. -- @field #string GRPC Use DCS-gRPC. MSRS.Backend = { SRSEXE = "srsexe", @@ -350,8 +393,8 @@ MSRS.Provider = { -- @field #string key Access key (DCS-gRPC with Google, ASW, AZURE as provider). -- @field #string secret Secret key (DCS-gRPC with ASW as provider) -- @field #string region Region. --- @field #string defaultVoice Default voice. --- @field #string voice +-- @field #string defaultVoice Default voice (not used). +-- @field #string voice Voice used. --- GRPC options. -- @type MSRS.GRPCOptions @@ -369,8 +412,8 @@ MSRS.Provider = { -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Refactoring of input/config file. --- TODO: Refactoring gRPC backend. +-- DONE: Refactoring of input/config file. +-- DONE: Refactoring gRPC backend. -- TODO: Add functions to remove freqs and modulations. -- DONE: Add coordinate. -- DONE: Add google. @@ -382,15 +425,16 @@ MSRS.Provider = { ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Create a new MSRS object. Required argument is the frequency and modulation. --- Other parameters are read from the `Moose_MSRS.lua` config file. If you do not have that file set up you must set up and use the SRS-TTS.exe (not DCS-gRPC) as backend, you need to still +-- Other parameters are read from the `Moose_MSRS.lua` config file. If you do not have that file set up you must set up and use the `DCS-SR-ExternalAudio.exe` (not DCS-gRPC) as backend, you need to still -- set the path to the exe file via @{#MSRS.SetPath}. -- -- @param #MSRS self +-- @param #string Path Path to SRS directory. Default `C:\\Program Files\\DCS-SimpleRadio-Standalone`. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. -- @param #string Backend Backend used: `MSRS.Backend.SRSEXE` (default) or `MSRS.Backend.GRPC`. -- @return #MSRS self -function MSRS:New(Frequency, Modulation, Backend) +function MSRS:New(Path, Frequency, Modulation, Backend) -- Defaults. Frequency = Frequency or 143 @@ -404,7 +448,7 @@ function MSRS:New(Frequency, Modulation, Backend) if not self.ConfigLoaded then -- Defaults. - self:SetPath() + self:SetPath(Path) self:SetPort() self:SetFrequencies(Frequency) self:SetModulations(Modulation) @@ -415,11 +459,18 @@ function MSRS:New(Frequency, Modulation, Backend) else - -- there might be some overwrites from :New() + -- Default wverwrites from :New() + + if Path then + self:SetPath(Path) + end if Frequency then - self:SetFrequencies(Frequency) - self:SetModulations(Modulation) + self:SetFrequencies(Frequency) + end + + if Modulation then + self:SetModulations(Modulation) end end @@ -440,7 +491,7 @@ end --- Set backend to communicate with SRS. -- There are two options: -- --- - `MSRS.Backend.SRSEXE`: This is the default and uses the SRS.exe. +-- - `MSRS.Backend.SRSEXE`: This is the default and uses the `DCS-SR-ExternalAudio.exe`. -- - `MSRS.Backend.GRPC`: Via DCS-gRPC. -- -- @param #MSRS self @@ -463,7 +514,7 @@ function MSRS:SetBackendGRPC() return self end ---- Set SRS-TTS.exe as backend to communicate with SRS. +--- Set `DCS-SR-ExternalAudio.exe` as backend to communicate with SRS. -- @param #MSRS self -- @return #MSRS self function MSRS:SetBackendSRSEXE(Backend) @@ -473,6 +524,17 @@ function MSRS:SetBackendSRSEXE(Backend) return self end +--- Set the default backend. +-- @param #MSRS self +function MSRS.SetDefaultBackend(Backend) + MSRS.backend=Backend or MSRS.Backend.SRSEXE +end + +--- Set DCS-gRPC to be the default backend. +-- @param #MSRS self +function MSRS.SetDefaultBackendGRPC() + MSRS.backend=MSRS.Backend.GRPC +end --- Get currently set backend. -- @param #MSRS self @@ -481,31 +543,25 @@ function MSRS:GetBackend() return self.backend end ---- Set path to SRS install directory. More precisely, path to where the DCS- +--- Set path to SRS install directory. More precisely, path to where the `DCS-SR-ExternalAudio.exe` is located. -- @param #MSRS self --- @param #string Path Path to the directory, where the sound file is located. +-- @param #string Path Path to the directory, where the sound file is located. Default is `C:\\Program Files\\DCS-SimpleRadio-Standalone`. -- @return #MSRS self function MSRS:SetPath(Path) - if Path==nil and not self.path then - self:E("ERROR: No path to SRS directory specified!") - return nil + -- Set path. + self.path=Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + + -- Remove (back)slashes. + local n=1 ; local nmax=1000 + while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do + self.path=self.path:sub(1,#self.path-1) + n=n+1 end - if Path then - -- Set path. - self.path=Path - - -- Remove (back)slashes. - local n=1 ; local nmax=1000 - while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do - self.path=self.path:sub(1,#self.path-1) - n=n+1 - end - - -- Debug output. - self:T(string.format("SRS path=%s", self:GetPath())) - end + -- Debug output. + self:T(string.format("SRS path=%s", self:GetPath())) + return self end @@ -669,7 +725,7 @@ end --- Set culture. -- @param #MSRS self --- @param #string Culture Culture, e.g. "en-GB". +-- @param #string Culture Culture, *e.g.* "en-GB". -- @return #MSRS self function MSRS:SetCulture(Culture) @@ -691,14 +747,59 @@ end --- Set to use a specific voice for a given provider. Note that this will override any gender and culture settings. -- @param #MSRS self --- @param #string Provider Provider. Default `MSRS.Provider.WINDOWS`. -- @param #string Voice Voice. +-- @param #string Provider Provider. Default is as set by @{#MSRS.SetProvider}, which itself defaults to `MSRS.Provider.WINDOWS` if not set. -- @return #MSRS self -function MSRS:SetProviderVoice(Provider, Voice) +function MSRS:SetVoiceProvider(Voice, Provider) self.poptions=self.poptions or {} - self.poptions[Provider or MSRS.Provider.WINDOWSo]=Voice + self.poptions[Provider or self:GetProvider()]=Voice + + return self +end + +--- Set to use a specific voice if Microsoft Windows' native TTS is use as provider. Note that this will override any gender and culture settings. +-- @param #MSRS self +-- @param #string Voice Voice. Default `"Microsoft Hazel Desktop"`. +-- @return #MSRS self +function MSRS:SetVoiceWindows(Voice) + + self:SetVoiceProvider(Voice or "Microsoft Hazel Desktop", MSRS.Provider.WINDOWS) + + return self +end + +--- Set to use a specific voice if Google is use as provider. Note that this will override any gender and culture settings. +-- @param #MSRS self +-- @param #string Voice Voice. Default `MSRS.Voices.Google.Standard.en_GB_Standard_A`. +-- @return #MSRS self +function MSRS:SetVoiceGoogle(Voice) + + self:SetVoiceProvider(Voice or MSRS.Voices.Google.Standard.en_GB_Standard_A, MSRS.Provider.GOOGLE) + + return self +end + + +--- Set to use a specific voice if Microsoft Azure is use as provider (only DCS-gRPC backend). Note that this will override any gender and culture settings. +-- @param #MSRS self +-- @param #string Voice [Azure Voice](https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). Default `"en-US-AriaNeural"`. +-- @return #MSRS self +function MSRS:SetVoiceAzure(Voice) + + self:SetVoiceProvider(Voice or "en-US-AriaNeural", MSRS.Provider.AZURE) + + return self +end + +--- Set to use a specific voice if Amazon Web Service is use as provider (only DCS-gRPC backend). Note that this will override any gender and culture settings. +-- @param #MSRS self +-- @param #string Voice [ASW Voice](https://docs.aws.amazon.com/polly/latest/dg/voicelist.html). Default `"Brian"`. +-- @return #MSRS self +function MSRS:SetVoiceAmazon(Voice) + + self:SetVoiceProvider(Voice or "Brian", MSRS.Provider.AMAZON) return self end @@ -706,12 +807,12 @@ end --- Get voice. -- @param #MSRS self -- @param #string Provider Provider. Default is the currently set provider (`self.provider`). --- @return #MSRS self +-- @return #string Voice. function MSRS:GetVoice(Provider) Provider=Provider or self.provider - if Provider then + if Provider and self.poptions[Provider] and self.poptions[Provider].voice then return self.poptions[Provider].voice else return self.voice @@ -730,7 +831,7 @@ function MSRS:SetCoordinate(Coordinate) return self end ---- Use google text-to-speech credentials. Also sets Google as default TTS provider. +--- **[Deprecated]** Use google text-to-speech credentials. Also sets Google as default TTS provider. -- @param #MSRS self -- @param #string PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". Can also be the Google API key. -- @return #MSRS self @@ -738,29 +839,30 @@ function MSRS:SetGoogle(PathToCredentials) if PathToCredentials then - self.google=PathToCredentials - self.APIKey=PathToCredentials - self.provider = "gcloud" - - self.GRPCOptions.DefaultProvider = "gcloud" - self.GRPCOptions.gcloud.key = PathToCredentials - self.ttsprovider = "Google" + self.provider = MSRS.Provider.GOOGLE + + self:SetProviderOptionsGoogle(PathToCredentials, PathToCredentials) end return self end ---- gRPC Backend: Use google text-to-speech set the API key. +--- **[Deprecated]** Use google text-to-speech set the API key (only for DCS-gRPC). -- @param #MSRS self -- @param #string APIKey API Key, usually a string of length 40 with characters and numbers. -- @return #MSRS self function MSRS:SetGoogleAPIKey(APIKey) if APIKey then - self.APIKey=APIKey - self.provider = "gcloud" - self.GRPCOptions.DefaultProvider = "gcloud" - self.GRPCOptions.gcloud.key = APIKey + + self.provider = MSRS.Provider.GOOGLE + + if self.poptions[MSRS.Provider.GOOGLE] then + self.poptions[MSRS.Provider.GOOGLE].key=APIKey + else + self:SetProviderOptionsGoogle(nil ,APIKey) + end + end return self end @@ -843,7 +945,7 @@ end --- Set provider options and credentials for Google Cloud. -- @param #MSRS self --- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. This is used if SRS-TTS.exe is used as backend. +-- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. This is used if `DCS-SR-ExternalAudio.exe` is used as backend. -- @param #string AccessKey Your API access key. This is necessary if DCS-gRPC is used as backend. -- @return #MSRS self function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) @@ -853,6 +955,32 @@ function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) return self end +--- Set provider options and credentials for Amazon Web Service (ASW). Only supported in combination with DCS-gRPC as backend. +-- @param #MSRS self +-- @param #string AccessKey Your API access key. +-- @param #string SecretKey Your secret key. +-- @param #string Region Your ASW [region](https://docs.aws.amazon.com/general/latest/gr/pol.html). +-- @return #MSRS self +function MSRS:SetProviderOptionsAmazon(AccessKey, SecretKey, Region) + + self:SetProviderOptions(MSRS.Provider.AMAZON, nil, AccessKey, SecretKey, Region) + + return self +end + +--- Set provider options and credentials for Microsoft Azure. Only supported in combination with DCS-gRPC as backend. +-- @param #MSRS self +-- @param #string AccessKey Your API access key. +-- @param #string Region Your Azure [region](https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). +-- @return #MSRS self +function MSRS:SetProviderOptionsAzure(AccessKey, Region) + + self:SetProviderOptions(MSRS.Provider.AZURE, nil, AccessKey, nil, Region) + + return self +end + + --- Get provider options. -- @param #MSRS self -- @param #string Provider Provider. Default is as set via @{#MSRS.SetProvider}. @@ -862,22 +990,40 @@ function MSRS:GetProviderOptions(Provider) end ---- Use Google text-to-speech as default. +--- Use Google to provide text-to-speech. -- @param #MSRS self -- @return #MSRS self function MSRS:SetTTSProviderGoogle() - self.ttsprovider = "Google" + self:SetProvider(MSRS.Provider.GOOGLE) return self end ---- Use Microsoft text-to-speech as default. +--- Use Microsoft to provide text-to-speech. -- @param #MSRS self -- @return #MSRS self function MSRS:SetTTSProviderMicrosoft() - self.ttsprovider = "Microsoft" + self:SetProvider(MSRS.Provider.WINDOWS) return self end + +--- Use Microsoft Azure to provide text-to-speech. Only supported if used in combination with DCS-gRPC as backend. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetTTSProviderAzure() + self:SetProvider(MSRS.Provider.AZURE) + return self +end + +--- Use Amazon Web Service (ASW) to provide text-to-speech. Only supported if used in combination with DCS-gRPC as backend. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetTTSProviderAmazon() + self:SetProvider(MSRS.Provider.AMAZON) + return self +end + + --- Print SRS STTS help to DCS log file. -- @param #MSRS self -- @return #MSRS self @@ -1322,10 +1468,9 @@ end -- @return #MSRS self function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) - --Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) - - self:I("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") - self:I({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) + -- Debug info. + self:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") + self:T({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) local options = {} -- #MSRS.GRPCOptions @@ -1369,35 +1514,23 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab local gender="" if self.gender then - --gender = ' gender=\"' .. self.gender .. '\"' gender=string.format(' gender=\"\%s\"', self.gender) end local language="" if self.culture then - --lang = ' language=\"' .. self.culture .. '\"' language=string.format(' language=\"\%s\"', self.culture) end - --- if self.culture or self.gender then --- preTag = '' --- postTag = '' --- ssml = preTag .. Text .. postTag --- end - + if self.gender or self.culture then ssml=string.format("%s", gender, language, Text) end end - env.info("FF freq") for _,freq in pairs(Frequencies) do - env.info("FF freq1") self:T("GRPC.tts") self:T(ssml) self:T(freq) self:T(options) - UTILS.PrintTableToLog(options) - env.info(UTILS.OneLineSerialize(options)) GRPC.tts(ssml, freq*1e6, options) end @@ -1418,36 +1551,46 @@ end -- 1) Create a config file named "Moose_MSRS.lua" at this location "C:\Users\\Saved Games\DCS\Config" (or wherever your Saved Games folder resides). -- 2) The file needs the following structure: -- --- -- Moose MSRS default Config --- MSRS_Config = { --- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- adjust as needed, note double \\ --- Port = 5002, -- adjust as needed --- Frequency = {127,243}, -- must be a table, 1..n entries! --- Modulation = {0,0}, -- must be a table, 1..n entries, one for each frequency! --- Volume = 1.0, -- 0.0 to 1.0 --- Coalition = 0, -- 0 = Neutral, 1 = Red, 2 = Blue --- Coordinate = {0,0,0}, -- x,y,altitude - optional, all in meters --- Culture = "en-GB", --- Gender = "male", --- Google = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- path to google json key file - optional. --- Label = "MSRS", --- Voice = "Microsoft Hazel Desktop", --- Provider = "Microsoft", -- this is the default TTS provider, e.g. "Google" or "Microsoft" --- -- gRPC (optional) --- GRPC = { -- see https://github.com/DCS-gRPC/rust-server --- coalition = "blue", -- blue, red, neutral --- DefaultProvider = "gcloud", -- win, gcloud, aws, or azure, some of the values below depend on your cloud provider --- gcloud = { --- key = "", -- for gRPC Google API key --- --secret = "", -- needed for aws --- --region = "",-- needed for aws --- defaultVoice = MSRS.Voices.Google.Standard.en_GB_Standard_F, --- }, --- win = { --- defaultVoice = "Hazel", --- }, --- } --- } +-- -- Moose MSRS default Config +-- MSRS_Config = { +-- Path = C:\\Program Files\\DCS-SimpleRadio-Standalone, -- Path to SRS install directory. +-- Port = 5002, -- Port of SRS server. Default 5002. +-- Backend = "srsexe", -- Interface to SRS: "srsexe" or "grpc". +-- Frequency = {127, 243}, -- Default frequences. Must be a table 1..n entries! +-- Modulation = {0,0}, -- Default modulations. Must be a table, 1..n entries, one for each frequency! +-- Volume = 1.0, -- Default volume [0,1]. +-- Coalition = 0, -- 0 = Neutral, 1 = Red, 2 = Blue (only a factor if SRS server has encryption enabled). +-- Coordinate = {0,0,0}, -- x, y, alt (only a factor if SRS server has line-of-sight and/or distance limit enabled). +-- Culture = "en-GB", +-- Gender = "male", +-- Voice = "Microsoft Hazel Desktop", -- Voice that is used if no explicit provider voice is specified. +-- Label = "MSRS", +-- Provider = "win", --Provider for generating TTS (win, gcloud, azure, asw). +-- +-- -- Windows +-- win = { +-- voice = "Microsoft Hazel Desktop", +-- }, +-- -- Google Cloud +-- gcloud = { +-- voice = "en-GB-Standard-A", -- The Google Cloud voice to use (see https://cloud.google.com/text-to-speech/docs/voices). +-- credentials="C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- Full path to credentials JSON file (only for SRS-TTS.exe backend) +-- key="Your access Key", -- Google API access key (only for DCS-gRPC backend) +-- }, +-- -- Amazon Web Service +-- aws = { +-- voice = "Brian", -- The default AWS voice to use (see https://docs.aws.amazon.com/polly/latest/dg/voicelist.html). +-- key="Your access Key", -- Your AWS key. +-- secret="Your secret key", -- Your AWS secret key. +-- region="eu-central-1", -- Your AWS region (see https://docs.aws.amazon.com/general/latest/gr/pol.html). +-- }, +-- -- Microsoft Azure +-- azure = { +-- voice="en-US-AriaNeural", --The default Azure voice to use (see https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). +-- key="Your access key", -- Your Azure access key. +-- region="westeurope", -- The Azure region to use (see https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). +-- }, +-- } -- -- 3) The config file is automatically loaded when Moose starts. YOu can also load the config into the MSRS raw class manually before you do anything else: -- @@ -1472,11 +1615,11 @@ end -- -- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS(243,radio.modulation.AM,nil,nil,MSRS.Voices.Google.Standard.fr_FR_Standard_C) -- --- -- Create new ATIS as usual --- atis=ATIS:New(AIRBASE.Caucasus.Batumi, 123, radio.modulation.AM) --- atis:SetSRS(nil,nil,nil,MSRS.Voices.Google.Standard.en_US_Standard_H) --- --Start ATIS --- atis:Start() +-- -- Create new ATIS as usual +-- atis=ATIS:New(AIRBASE.Caucasus.Batumi, 123, radio.modulation.AM) +-- atis:SetSRS(nil,nil,nil,MSRS.Voices.Google.Standard.en_US_Standard_H) +-- --Start ATIS +-- atis:Start() function MSRS:LoadConfigFile(Path,Filename) if lfs == nil then @@ -1502,6 +1645,7 @@ function MSRS:LoadConfigFile(Path,Filename) Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" Self.port = MSRS_Config.Port or 5002 + Self.backend = MSRS_Config.Backend or MSRS.Backend.SRSEXE Self.frequencies = MSRS_Config.Frequency or {127,243} Self.modulations = MSRS_Config.Modulation or {0,0} Self.coalition = MSRS_Config.Coalition or 0 @@ -1522,69 +1666,6 @@ function MSRS:LoadConfigFile(Path,Filename) Self.ConfigLoaded = true - if false then - - if self then - self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - self.port = MSRS_Config.Port or 5002 - self.frequencies = MSRS_Config.Frequency or {127,243} - self.modulations = MSRS_Config.Modulation or {0,0} - self.coalition = MSRS_Config.Coalition or 0 - if MSRS_Config.Coordinate then - self.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) - end - self.culture = MSRS_Config.Culture or "en-GB" - self.gender = MSRS_Config.Gender or "male" - self.google = MSRS_Config.Google - if MSRS_Config.Provider then - self.ttsprovider = MSRS_Config.Provider - end - self.Label = MSRS_Config.Label or "MSRS" - self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel - - if MSRS_Config.GRPC then - self.provider = MSRS_Config.GRPC.DefaultProvider - if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then - self.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key - self.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice - self.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret - self.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region - end - end - - self.ConfigLoaded = true - else - - MSRS.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - MSRS.port = MSRS_Config.Port or 5002 - MSRS.frequencies = MSRS_Config.Frequency or {127,243} - MSRS.modulations = MSRS_Config.Modulation or {0,0} - MSRS.coalition = MSRS_Config.Coalition or 0 - if MSRS_Config.Coordinate then - MSRS.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) - end - MSRS.culture = MSRS_Config.Culture or "en-GB" - MSRS.gender = MSRS_Config.Gender or "male" - MSRS.google = MSRS_Config.Google - if MSRS_Config.Provider then - MSRS.ttsprovider = MSRS_Config.Provider - end - MSRS.Label = MSRS_Config.Label or "MSRS" - MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel - if MSRS_Config.GRPC then - MSRS.provider = MSRS_Config.GRPC.DefaultProvider - if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then - MSRS.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key - MSRS.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice - MSRS.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret - MSRS.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region - end - end - MSRS.ConfigLoaded = true - end - - end - end env.info("MSRS - Successfully loaded default configuration from disk!",false) end From 5f7115f4fef9656fcd4e3278b724c34fd65095cc Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 23 Dec 2023 16:53:42 +0100 Subject: [PATCH 10/10] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 899d41fba..36a300b67 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -986,7 +986,7 @@ end -- @param #string Provider Provider. Default is as set via @{#MSRS.SetProvider}. -- @return #MSRS.ProviderOptions Provider options. function MSRS:GetProviderOptions(Provider) - return self.poptions[Provider or self.provider] + return self.poptions[Provider or self.provider] or {} end