Merge remote-tracking branch 'origin/master' into develop

# Conflicts:
#	Moose Development/Moose/Sound/SRS.lua
This commit is contained in:
Applevangelist 2023-12-31 17:26:28 +01:00
commit 8fc6f511c8
3 changed files with 228 additions and 208 deletions

View File

@ -1986,7 +1986,7 @@ do
MGroupInclude = MGroupInclude and MGroupActive
end
if self.Filter.Coalitions then
if self.Filter.Coalitions and MGroupInclude then
local MGroupCoalition = false
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
@ -1997,7 +1997,7 @@ do
MGroupInclude = MGroupInclude and MGroupCoalition
end
if self.Filter.Categories then
if self.Filter.Categories and MGroupInclude then
local MGroupCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
self:T3( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } )
@ -2008,7 +2008,7 @@ do
MGroupInclude = MGroupInclude and MGroupCategory
end
if self.Filter.Countries then
if self.Filter.Countries and MGroupInclude then
local MGroupCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
self:T3( { "Country:", MGroup:GetCountry(), CountryName } )
@ -2019,7 +2019,7 @@ do
MGroupInclude = MGroupInclude and MGroupCountry
end
if self.Filter.GroupPrefixes then
if self.Filter.GroupPrefixes and MGroupInclude then
local MGroupPrefix = false
for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do
self:T3( { "Prefix:", string.find( MGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } )
@ -2030,7 +2030,7 @@ do
MGroupInclude = MGroupInclude and MGroupPrefix
end
if self.Filter.Zones then
if self.Filter.Zones and MGroupInclude then
local MGroupZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
--self:T( "Zone:", ZoneName )
@ -2041,7 +2041,7 @@ do
MGroupInclude = MGroupInclude and MGroupZone
end
if self.Filter.Functions then
if self.Filter.Functions and MGroupInclude then
local MGroupFunc = false
MGroupFunc = self:_EvalFilterFunctions(MGroup)
MGroupInclude = MGroupInclude and MGroupFunc
@ -3192,7 +3192,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitActive
end
if self.Filter.Coalitions then
if self.Filter.Coalitions and MUnitInclude then
local MUnitCoalition = false
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
self:F( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
@ -3203,7 +3203,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitCoalition
end
if self.Filter.Categories then
if self.Filter.Categories and MUnitInclude then
local MUnitCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } )
@ -3214,7 +3214,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitCategory
end
if self.Filter.Types then
if self.Filter.Types and MUnitInclude then
local MUnitType = false
for TypeID, TypeName in pairs( self.Filter.Types ) do
self:T3( { "Type:", MUnit:GetTypeName(), TypeName } )
@ -3225,7 +3225,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitType
end
if self.Filter.Countries then
if self.Filter.Countries and MUnitInclude then
local MUnitCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
self:T3( { "Country:", MUnit:GetCountry(), CountryName } )
@ -3236,7 +3236,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitCountry
end
if self.Filter.UnitPrefixes then
if self.Filter.UnitPrefixes and MUnitInclude then
local MUnitPrefix = false
for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do
self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } )
@ -3247,7 +3247,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitPrefix
end
if self.Filter.RadarTypes then
if self.Filter.RadarTypes and MUnitInclude then
local MUnitRadar = false
for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do
self:T3( { "Radar:", RadarType } )
@ -3261,7 +3261,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitRadar
end
if self.Filter.SEAD then
if self.Filter.SEAD and MUnitInclude then
local MUnitSEAD = false
if MUnit:HasSEAD() == true then
self:T3( "SEAD Found" )
@ -3271,7 +3271,7 @@ do -- SET_UNIT
end
end
if self.Filter.Zones then
if self.Filter.Zones and MUnitInclude then
local MGroupZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
self:T3( "Zone:", ZoneName )
@ -3282,7 +3282,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MGroupZone
end
if self.Filter.Functions then
if self.Filter.Functions and MUnitInclude then
local MUnitFunc = self:_EvalFilterFunctions(MUnit)
MUnitInclude = MUnitInclude and MUnitFunc
end
@ -4689,7 +4689,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientActive
end
if self.Filter.Coalitions then
if self.Filter.Coalitions and MClientInclude then
local MClientCoalition = false
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName )
@ -4702,7 +4702,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientCoalition
end
if self.Filter.Categories then
if self.Filter.Categories and MClientInclude then
local MClientCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName )
@ -4715,7 +4715,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientCategory
end
if self.Filter.Types then
if self.Filter.Types and MClientInclude then
local MClientType = false
for TypeID, TypeName in pairs( self.Filter.Types ) do
self:T3( { "Type:", MClient:GetTypeName(), TypeName } )
@ -4727,7 +4727,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientType
end
if self.Filter.Countries then
if self.Filter.Countries and MClientInclude then
local MClientCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName )
@ -4740,7 +4740,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientCountry
end
if self.Filter.ClientPrefixes then
if self.Filter.ClientPrefixes and MClientInclude then
local MClientPrefix = false
for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do
self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } )
@ -4752,7 +4752,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientPrefix
end
if self.Filter.Zones then
if self.Filter.Zones and MClientInclude then
local MClientZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
self:T3( "Zone:", ZoneName )
@ -4764,7 +4764,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientZone
end
if self.Filter.Playernames then
if self.Filter.Playernames and MClientInclude then
local MClientPlayername = false
local playername = MClient:GetPlayerName() or "Unknown"
--self:T(playername)
@ -4777,7 +4777,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientPlayername
end
if self.Filter.Callsigns then
if self.Filter.Callsigns and MClientInclude then
local MClientCallsigns = false
local callsign = MClient:GetCallsign()
--self:I(callsign)
@ -4790,7 +4790,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientCallsigns
end
if self.Filter.Functions then
if self.Filter.Functions and MClientInclude then
local MClientFunc = self:_EvalFilterFunctions(MClient)
MClientInclude = MClientInclude and MClientFunc
end
@ -5554,7 +5554,7 @@ do -- SET_AIRBASE
MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition
end
if self.Filter.Categories then
if self.Filter.Categories and MAirbaseInclude then
local MAirbaseCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName )
@ -7900,7 +7900,7 @@ do -- SET_OPSGROUP
end
-- Filter coalitions.
if self.Filter.Coalitions then
if self.Filter.Coalitions and MGroupInclude then
local MGroupCoalition = false
@ -7914,7 +7914,7 @@ do -- SET_OPSGROUP
end
-- Filter categories.
if self.Filter.Categories then
if self.Filter.Categories and MGroupInclude then
local MGroupCategory = false
@ -7928,7 +7928,7 @@ do -- SET_OPSGROUP
end
-- Filter countries.
if self.Filter.Countries then
if self.Filter.Countries and MGroupInclude then
local MGroupCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
if country.id[CountryName] == MGroup:GetCountry() then
@ -7939,7 +7939,7 @@ do -- SET_OPSGROUP
end
-- Filter "prefixes".
if self.Filter.GroupPrefixes then
if self.Filter.GroupPrefixes and MGroupInclude then
local MGroupPrefix = false

View File

@ -109,10 +109,10 @@
--
-- 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.
--
--
-- 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
@ -134,32 +134,32 @@
-- ## 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 (AWS) for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsAmazon} functions:
@ -172,21 +172,21 @@
-- You can set the voice to use with AWS 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
--
-- 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.
@ -446,13 +446,15 @@ end
-- @return #MSRS self
function MSRS:New(Path, Frequency, Modulation, Backend)
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New()) -- #MSRS
self:F( {Path, Frequency, Modulation, Backend} )
-- Defaults.
Frequency = Frequency or 143
Modulation = Modulation or radio.modulation.AM
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New()) -- #MSRS
self.lid = string.format("%s-%s | ", "unknown", self.version)
if not self.ConfigLoaded then
@ -466,6 +468,7 @@ function MSRS:New(Path, Frequency, Modulation, Backend)
self:SetCoalition()
self:SetLabel()
self:SetVolume()
self:SetBackend(Backend)
else
@ -483,6 +486,10 @@ function MSRS:New(Path, Frequency, Modulation, Backend)
self:SetModulations(Modulation)
end
if Backend then
self:SetBackend(Backend)
end
end
self.lid = string.format("%s-%s | ", self.name, self.version)
@ -508,7 +515,7 @@ end
-- @param #string Backend Backend used. Default is `MSRS.Backend.SRSEXE`.
-- @return #MSRS self
function MSRS:SetBackend(Backend)
self:F( {Backend=Backend} )
self.backend=Backend or MSRS.Backend.SRSEXE
return self
@ -518,7 +525,7 @@ end
-- @param #MSRS self
-- @return #MSRS self
function MSRS:SetBackendGRPC()
self:F()
self:SetBackend(MSRS.Backend.GRPC)
return self
@ -527,8 +534,8 @@ end
--- Set `DCS-SR-ExternalAudio.exe` as backend to communicate with SRS.
-- @param #MSRS self
-- @return #MSRS self
function MSRS:SetBackendSRSEXE(Backend)
function MSRS:SetBackendSRSEXE()
self:F()
self:SetBackend(MSRS.Backend.SRSEXE)
return self
@ -537,12 +544,14 @@ end
--- Set the default backend.
-- @param #MSRS self
function MSRS.SetDefaultBackend(Backend)
self:F( {Backend=Backend} )
MSRS.backend=Backend or MSRS.Backend.SRSEXE
end
--- Set DCS-gRPC to be the default backend.
-- @param #MSRS self
function MSRS.SetDefaultBackendGRPC()
self:F()
MSRS.backend=MSRS.Backend.GRPC
end
@ -558,6 +567,7 @@ end
-- @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)
self:F( {Path=Path} )
-- Set path.
self.path=Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
@ -570,8 +580,8 @@ function MSRS:SetPath(Path)
end
-- Debug output.
self:T(string.format("SRS path=%s", self:GetPath()))
self:F(string.format("SRS path=%s", self:GetPath()))
return self
end
@ -587,6 +597,7 @@ end
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
-- @return #MSRS self
function MSRS:SetVolume(Volume)
self:F( {Volume=Volume} )
local volume = Volume or 1
if volume > 1 then volume = 1 elseif volume < 0 then volume = 0 end
self.volume = volume
@ -605,6 +616,7 @@ end
-- @param #number Label. Default "ROBOT"
-- @return #MSRS self
function MSRS:SetLabel(Label)
self:F( {Label=Label} )
self.Label=Label or "ROBOT"
return self
end
@ -621,6 +633,7 @@ end
-- @param #number Port Port. Default 5002.
-- @return #MSRS self
function MSRS:SetPort(Port)
self:F( {Port=Port} )
self.port=Port or 5002
self:T(string.format("SRS port=%s", self:GetPort()))
return self
@ -638,6 +651,7 @@ end
-- @param #number Coalition Coalition. Default 0.
-- @return #MSRS self
function MSRS:SetCoalition(Coalition)
self:F( {Coalition=Coalition} )
self.coalition=Coalition or 0
return self
end
@ -655,7 +669,7 @@ 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)
self:F( Frequencies )
self.frequencies=UTILS.EnsureTable(Frequencies, false)
return self
@ -666,7 +680,7 @@ 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:AddFrequencies(Frequencies)
self:F( Frequencies )
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)
@ -688,7 +702,7 @@ 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)
self:F( Modulations )
self.modulations=UTILS.EnsureTable(Modulations, false)
-- Debug info.
@ -703,7 +717,7 @@ 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)
self:F( Modulations )
for _,_mod in pairs(UTILS.EnsureTable(Modulations, false)) do
table.insert(self.modulations,_mod)
end
@ -723,7 +737,7 @@ end
-- @param #string Gender Gender: "male" or "female" (default).
-- @return #MSRS self
function MSRS:SetGender(Gender)
self:F( {Gender=Gender} )
Gender=Gender or "female"
self.gender=Gender:lower()
@ -739,7 +753,7 @@ end
-- @param #string Culture Culture, *e.g.* "en-GB".
-- @return #MSRS self
function MSRS:SetCulture(Culture)
self:F( {Culture=Culture} )
self.culture=Culture
return self
@ -750,7 +764,7 @@ end
-- @param #string Voice Voice.
-- @return #MSRS self
function MSRS:SetVoice(Voice)
self:F( {Voice=Voice} )
self.voice=Voice
return self
@ -762,9 +776,9 @@ end
-- @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:SetVoiceProvider(Voice, Provider)
self:F( {Voice=Voice, Provider=Provider} )
self.poptions=self.poptions or {}
self.poptions[Provider or self:GetProvider()]=Voice
return self
@ -775,7 +789,7 @@ end
-- @param #string Voice Voice. Default `"Microsoft Hazel Desktop"`.
-- @return #MSRS self
function MSRS:SetVoiceWindows(Voice)
self:F( {Voice=Voice} )
self:SetVoiceProvider(Voice or "Microsoft Hazel Desktop", MSRS.Provider.WINDOWS)
return self
@ -786,7 +800,7 @@ end
-- @param #string Voice Voice. Default `MSRS.Voices.Google.Standard.en_GB_Standard_A`.
-- @return #MSRS self
function MSRS:SetVoiceGoogle(Voice)
self:F( {Voice=Voice} )
self:SetVoiceProvider(Voice or MSRS.Voices.Google.Standard.en_GB_Standard_A, MSRS.Provider.GOOGLE)
return self
@ -798,7 +812,7 @@ end
-- @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:F( {Voice=Voice} )
self:SetVoiceProvider(Voice or "en-US-AriaNeural", MSRS.Provider.AZURE)
return self
@ -809,7 +823,7 @@ end
-- @param #string Voice [AWS Voice](https://docs.aws.amazon.com/polly/latest/dg/voicelist.html). Default `"Brian"`.
-- @return #MSRS self
function MSRS:SetVoiceAmazon(Voice)
self:F( {Voice=Voice} )
self:SetVoiceProvider(Voice or "Brian", MSRS.Provider.AMAZON)
return self
@ -822,7 +836,7 @@ end
function MSRS:GetVoice(Provider)
Provider=Provider or self.provider
if Provider and self.poptions[Provider] and self.poptions[Provider].voice then
return self.poptions[Provider].voice
else
@ -836,7 +850,7 @@ end
-- @param Core.Point#COORDINATE Coordinate Origin of the transmission.
-- @return #MSRS self
function MSRS:SetCoordinate(Coordinate)
self:F( Coordinate )
self.coordinate=Coordinate
return self
@ -847,11 +861,11 @@ end
-- @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
function MSRS:SetGoogle(PathToCredentials)
self:F( {PathToCredentials=PathToCredentials} )
if PathToCredentials then
self.provider = MSRS.Provider.GOOGLE
self:SetProviderOptionsGoogle(PathToCredentials, PathToCredentials)
end
@ -864,16 +878,17 @@ end
-- @param #string APIKey API Key, usually a string of length 40 with characters and numbers.
-- @return #MSRS self
function MSRS:SetGoogleAPIKey(APIKey)
self:F( {APIKey=APIKey} )
if APIKey then
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
@ -881,18 +896,19 @@ 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:F( {Provider=Provider} )
self.provider = Provider or MSRS.Provider.WINDOWS
return self
end
@ -914,21 +930,15 @@ end
-- @param #string Region Region to use.
-- @return #MSRS.ProviderOptions Provider optionas table.
function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region)
self:F( {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
@ -942,7 +952,7 @@ end
-- @param #string Region Region to use.
-- @return #MSRS.ProviderOptions Provider optionas table.
function MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region)
self:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} )
local option={} --#MSRS.ProviderOptions
option.provider=Provider
@ -960,9 +970,8 @@ end
-- @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:F( {CredentialsFile, AccessKey} )
self:SetProviderOptions(MSRS.Provider.GOOGLE, CredentialsFile, AccessKey)
return self
end
@ -973,9 +982,8 @@ end
-- @param #string Region Your AWS [region](https://docs.aws.amazon.com/general/latest/gr/pol.html).
-- @return #MSRS self
function MSRS:SetProviderOptionsAmazon(AccessKey, SecretKey, Region)
self:F( {AccessKey, SecretKey, Region} )
self:SetProviderOptions(MSRS.Provider.AMAZON, nil, AccessKey, SecretKey, Region)
return self
end
@ -985,9 +993,8 @@ end
-- @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:F( {AccessKey, Region} )
self:SetProviderOptions(MSRS.Provider.AZURE, nil, AccessKey, nil, Region)
return self
end
@ -1005,6 +1012,7 @@ end
-- @param #MSRS self
-- @return #MSRS self
function MSRS:SetTTSProviderGoogle()
self:F()
self:SetProvider(MSRS.Provider.GOOGLE)
return self
end
@ -1013,6 +1021,7 @@ end
-- @param #MSRS self
-- @return #MSRS self
function MSRS:SetTTSProviderMicrosoft()
self:F()
self:SetProvider(MSRS.Provider.WINDOWS)
return self
end
@ -1022,6 +1031,7 @@ end
-- @param #MSRS self
-- @return #MSRS self
function MSRS:SetTTSProviderAzure()
self:F()
self:SetProvider(MSRS.Provider.AZURE)
return self
end
@ -1030,6 +1040,7 @@ end
-- @param #MSRS self
-- @return #MSRS self
function MSRS:SetTTSProviderAmazon()
self:F()
self:SetProvider(MSRS.Provider.AMAZON)
return self
end
@ -1039,7 +1050,7 @@ end
-- @param #MSRS self
-- @return #MSRS self
function MSRS:Help()
self:F()
-- Path and exe.
local path=self:GetPath()
local exe="DCS-SR-ExternalAudio.exe"
@ -1074,14 +1085,22 @@ end
-- @param #number Delay Delay in seconds, before the sound file is played.
-- @return #MSRS self
function MSRS:PlaySoundFile(Soundfile, Delay)
self:F( {Soundfile, Delay} )
-- Sound file name.
local soundfile=Soundfile:GetName()
-- First check if text file exists!
local exists=UTILS.FileExists(soundfile)
if not exists then
self:E("ERROR: MSRS sound file does not exist! File="..soundfile)
return self
end
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MSRS.PlaySoundFile, self, Soundfile, 0)
else
-- Sound file name.
local soundfile=Soundfile:GetName()
-- Get command.
local command=self:_GetCommand()
@ -1102,26 +1121,25 @@ end
-- @param #number Delay Delay in seconds, before the sound file is played.
-- @return #MSRS self
function MSRS:PlaySoundText(SoundText, Delay)
self:F( {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)
end
end
@ -1136,20 +1154,17 @@ end
-- @param Core.Point#COORDINATE Coordinate Coordinate.
-- @return #MSRS self
function MSRS:PlayText(Text, Delay, Coordinate)
self:F( {Text, Delay, Coordinate} )
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate)
else
if self.backend==MSRS.Backend.GRPC then
self:T(self.lid.."Transmitting")
self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate)
else
self:PlayTextExt(Text, Delay, nil, nil, nil, nil, nil, nil, nil, Coordinate)
end
end
@ -1171,29 +1186,30 @@ end
-- @param Core.Point#COORDINATE Coordinate Coordinate.
-- @return #MSRS self
function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate)
self:F( {Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate} )
if Delay and Delay>0 then
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.
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, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate)
end
end
@ -1208,6 +1224,7 @@ end
-- @param #number Delay Delay in seconds, before the message is played.
-- @return #MSRS self
function MSRS:PlayTextFile(TextFile, Delay)
self:F( {TextFile, Delay} )
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MSRS.PlayTextFile, self, TextFile, 0)
@ -1249,10 +1266,11 @@ 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 (or 0 if no input coordinate was given).
-- @return #number Longitude (or 0 if no input coordinate was given).
-- @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)
self:F( {Coordinate=Coordinate} )
local lat=0.0
local lon=0.0
@ -1284,6 +1302,7 @@ end
-- @param Core.Point#COORDINATE coordinate Coordinate.
-- @return #string Command.
function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port, label, coordinate)
self:F( {freqs, modus, coal, gender, voice, culture, volume, speed, port, label, coordinate} )
local path=self:GetPath()
local exe="DCS-SR-ExternalAudio.exe"
@ -1332,7 +1351,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
-- Set provider options
if self.provider==MSRS.Provider.GOOGLE then
local pops=self:GetProviderOptions()
local pops=self:GetProviderOptions()
command=command..string.format(' --ssml -G "%s"', pops.credentials)
elseif self.provider==MSRS.Provider.WINDOWS then
-- Nothing to do.
@ -1356,82 +1375,83 @@ end
-- @param #string command Command to executer
-- @return #number Return value of os.execute() command.
function MSRS:_ExecCommand(command)
self:F( {command=command} )
-- Skip this function if _GetCommand was not able to find the executable
if string.find(command, "CommandNotFound") then return 0 end
-- Skip this function if _GetCommand was not able to find the executable
if string.find(command, "CommandNotFound") then return 0 end
local batContent = command.." && exit"
-- Create a tmp file.
local filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".bat"
local script=io.open(filename, "w+")
script:write(batContent)
script:close()
self:T("MSRS batch file created: "..filename)
self:T("MSRS batch content: "..batContent)
local res=nil
if true then
local batContent = command.." && exit"
-- Create a tmp file.
local filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".bat"
local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs"
local script=io.open(filename, "w+")
script:write(batContent)
-- VBS script
local script = io.open(filenvbs, "w+")
script:write(string.format('Dim WinScriptHost\n'))
script:write(string.format('Set WinScriptHost = CreateObject("WScript.Shell")\n'))
script:write(string.format('WinScriptHost.Run Chr(34) & "%s" & Chr(34), 0\n', filename))
script:write(string.format('Set WinScriptHost = Nothing'))
script:close()
self:T("MSRS vbs file created to start batch="..filenvbs)
-- Run visual basic script. This still pops up a window but very briefly and does not put the DCS window out of focus.
local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs)
-- Debug output.
self:T("MSRS execute VBS command="..runvbs)
-- Play file in 0.01 seconds
res=os.execute(runvbs)
-- Remove file in 1 second.
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1)
self:T("MSRS vbs and batch file removed")
elseif false then
-- Create a tmp file.
local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs"
-- VBS script
local script = io.open(filenvbs, "w+")
script:write(string.format('Set oShell = CreateObject ("Wscript.Shell")\n'))
script:write(string.format('Dim strArgs\n'))
script:write(string.format('strArgs = "cmd /c %s"\n', filename))
script:write(string.format('oShell.Run strArgs, 0, false'))
script:close()
self:T("MSRS batch file created: "..filename)
self:T("MSRS batch content: "..batContent)
local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs)
local res=nil
if true then
-- Play file in 0.01 seconds
res=os.execute(runvbs)
-- Create a tmp file.
local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs"
else
-- Play command.
command=string.format('start /b "" "%s"', filename)
-- VBS script
local script = io.open(filenvbs, "w+")
script:write(string.format('Dim WinScriptHost\n'))
script:write(string.format('Set WinScriptHost = CreateObject("WScript.Shell")\n'))
script:write(string.format('WinScriptHost.Run Chr(34) & "%s" & Chr(34), 0\n', filename))
script:write(string.format('Set WinScriptHost = Nothing'))
script:close()
self:T("MSRS vbs file created to start batch="..filenvbs)
-- Debug output.
self:T("MSRS execute command="..command)
-- Run visual basic script. This still pops up a window but very briefly and does not put the DCS window out of focus.
local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs)
-- Execute command
res=os.execute(command)
-- Debug output.
self:T("MSRS execute VBS command="..runvbs)
-- Remove file in 1 second.
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
-- Play file in 0.01 seconds
res=os.execute(runvbs)
-- Remove file in 1 second.
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1)
self:T("MSRS vbs and batch file removed")
elseif false then
-- Create a tmp file.
local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs"
-- VBS script
local script = io.open(filenvbs, "w+")
script:write(string.format('Set oShell = CreateObject ("Wscript.Shell")\n'))
script:write(string.format('Dim strArgs\n'))
script:write(string.format('strArgs = "cmd /c %s"\n', filename))
script:write(string.format('oShell.Run strArgs, 0, false'))
script:close()
local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs)
-- Play file in 0.01 seconds
res=os.execute(runvbs)
else
-- Play command.
command=string.format('start /b "" "%s"', filename)
-- Debug output.
self:T("MSRS execute command="..command)
-- Execute command
res=os.execute(command)
-- Remove file in 1 second.
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
end
end
return res
@ -1490,19 +1510,19 @@ end
function MSRS:_DCSgRPCtts(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})
self:F("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()")
self:F({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate})
local options = {} -- #MSRS.GRPCOptions
local ssml = Text or ''
-- Get frequenceies.
Frequencies = UTILS.EnsureTable(Frequencies, true) or self:GetFrequencies()
-- Plain text (not really used.
options.plaintext=Text
-- Name shows as sender.
options.srsClientName = Label or self.Label
@ -1510,18 +1530,19 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab
if self.coordinate then
options.position = {}
options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate)
end
end
-- Coalition (gRPC expects lower case)
options.coalition = UTILS.GetCoalitionName(self.coalition):lower()
-- Provider (win, gcloud, ...)
local provider = self.provider or MSRS.Provider.WINDOWS
self:F({provider=provider})
-- Provider options: voice, credentials
options.provider = {}
options.provider[provider] = self:GetProviderOptions(provider)
-- Voice
Voice=Voice or self:GetVoice(self.provider) or self.voice
@ -1529,11 +1550,10 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab
-- We use a specific voice
options.provider[provider].voice = Voice
else
-- DCS-gRPC doesn't directly support language/gender, but can use SSML
local preTag, genderProp, langProp, postTag = '', '', '', ''
local gender=""
if self.gender then
gender=string.format(' gender=\"%s\"', self.gender)
@ -1547,12 +1567,11 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab
ssml=string.format("<voice%s%s>%s</voice>", gender, language, Text)
end
end
for _,freq in pairs(Frequencies) do
self:T("GRPC.tts")
self:T(ssml)
self:T(freq)
self:T(options)
self:F("Calling GRPC.tts with the following parameter:")
self:F({ssml=ssml, freq=freq, options=options})
self:F(options.provider[provider])
GRPC.tts(ssml, freq*1e6, options)
end
@ -1592,7 +1611,7 @@ end
-- -- 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).
@ -1608,7 +1627,7 @@ end
-- },
-- -- 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).
-- 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).
-- },
@ -1655,16 +1674,16 @@ function MSRS:LoadConfigFile(Path,Filename)
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))()
if MSRS_Config then
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.backend = MSRS_Config.Backend or MSRS.Backend.SRSEXE
@ -1685,13 +1704,13 @@ function MSRS:LoadConfigFile(Path,Filename)
Self.poptions[provider]=MSRS_Config[provider]
end
end
Self.ConfigLoaded = true
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

View File

@ -523,6 +523,7 @@ ENUMS.ReportingName =
Hawkeye = "E-2D",
Sentry = "E-3A",
Stratotanker = "KC-135",
Gasstation = "KC-135MPRS",
Extender = "KC-10",
Orion = "P-3C",
Viking = "S-3B",