From b88a421d2c79d6d210e6e82f8c5ce81916c12077 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 5 Mar 2026 10:16:00 +0100 Subject: [PATCH 1/7] xx --- Moose Development/Moose/Core/Spawn.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 45af7269a..0a64a7d86 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -802,7 +802,7 @@ end --- [Airplane - F15/16/18/AWACS/B1B/Tanker only] Set the STN Link16 starting number of the Group; each unit of the spawned group will have a consecutive STN set. -- @param #SPAWN self --- @param #number Octal The octal number (digits 1..7, max 5 digits, i.e. 1..77777) to set the STN to. Every STN needs to be unique! +-- @param #number Octal The octal number (digits 0..7, max 5 digits, i.e. 1..77777, cannot be zero) to set the STN to. Every STN needs to be unique! -- @return #SPAWN self function SPAWN:InitSTN(Octal) --self:F( { Octal = Octal } ) @@ -820,7 +820,7 @@ end --- [Airplane - A10-C II only] Set the SADL TN starting number of the Group; each unit of the spawned group will have a consecutive SADL set. -- @param #SPAWN self --- @param #number Octal The octal number (digits 1..7, max 4 digits, i.e. 1..7777) to set the SADL to. Every SADL needs to be unique! +-- @param #number Octal The octal number (digits 0..7, max 4 digits, i.e. 1..7777, cannot be zero) to set the SADL to. Every SADL needs to be unique! -- @return #SPAWN self function SPAWN:InitSADL(Octal) --self:F( { Octal = Octal } ) From 3514073433e1c68496b72dda6d0ecdef9aa6514a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 5 Mar 2026 10:17:09 +0100 Subject: [PATCH 2/7] xx --- .../Moose/Functional/ZoneCaptureCoalition.lua | 62 +++++-------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index 256e36d00..c5d3817d3 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -48,7 +48,7 @@ do -- ZONE_CAPTURE_COALITION - -- @type ZONE_CAPTURE_COALITION + --- @type ZONE_CAPTURE_COALITION -- @field #string ClassName Name of the class. -- @field #number MarkBlue ID of blue F10 mark. -- @field #number MarkRed ID of red F10 mark. @@ -220,36 +220,6 @@ do -- ZONE_CAPTURE_COALITION -- A capture zone has been setup that guards the presence of the troops. -- Troops are guarded by red forces. Blue is required to destroy the red forces and capture the zones. -- - -- At first, we setup the Command Centers - -- - -- do - -- - -- RU_CC = COMMANDCENTER:New( GROUP:FindByName( "REDHQ" ), "Russia HQ" ) - -- US_CC = COMMANDCENTER:New( GROUP:FindByName( "BLUEHQ" ), "USA HQ" ) - -- - -- end - -- - -- Next, we define the mission, and add some scoring to it. - -- - -- do -- Missions - -- - -- US_Mission_EchoBay = MISSION:New( US_CC, "Echo Bay", "Primary", - -- "Welcome trainee. The airport Groom Lake in Echo Bay needs to be captured.\n" .. - -- "There are five random capture zones located at the airbase.\n" .. - -- "Move to one of the capture zones, destroy the fuel tanks in the capture zone, " .. - -- "and occupy each capture zone with a platoon.\n " .. - -- "Your orders are to hold position until all capture zones are taken.\n" .. - -- "Use the map (F10) for a clear indication of the location of each capture zone.\n" .. - -- "Note that heavy resistance can be expected at the airbase!\n" .. - -- "Mission 'Echo Bay' is complete when all five capture zones are taken, and held for at least 5 minutes!" - -- , coalition.side.RED ) - -- - -- US_Mission_EchoBay:Start() - -- - -- end - -- - -- - -- Now the real work starts. -- We define a **CaptureZone** object, which is a ZONE object. -- Within the mission, a trigger zone is created with the name __CaptureZone__, with the defined radius within the mission editor. -- @@ -280,12 +250,12 @@ do -- ZONE_CAPTURE_COALITION -- self:E( { Coalition = Coalition } ) -- if Coalition == coalition.side.BLUE then -- ZoneCaptureCoalition:Smoke( SMOKECOLOR.Blue ) - -- US_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) - -- RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of the USA", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) + -- MESSAGE:New(string.format( "%s is under protection of the USA", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE) + -- MESSAGE:New(string.format( "%s is under protection of the USA", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED) -- else -- ZoneCaptureCoalition:Smoke( SMOKECOLOR.Red ) - -- RU_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) - -- US_CC:MessageTypeToCoalition( string.format( "%s is under protection of Russia", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) + -- MESSAGE:New(string.format( "%s is under protection of Russia", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE) + -- MESSAGE:New(string.format( "%s is under protection of Russia", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED) -- end -- end -- end @@ -297,8 +267,8 @@ do -- ZONE_CAPTURE_COALITION -- -- @param Functional.Protect#ZONE_CAPTURE_COALITION self -- function ZoneCaptureCoalition:OnEnterEmpty() -- self:Smoke( SMOKECOLOR.Green ) - -- US_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) - -- RU_CC:MessageTypeToCoalition( string.format( "%s is unprotected, and can be captured!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) + -- MESSAGE:New(string.format( "%s is unprotected, and can be captured!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE) + -- MESSAGE:New(string.format( "%s is unprotected, and can be captured!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED) -- end -- -- The next Event Handlers speak for itself. @@ -310,11 +280,11 @@ do -- ZONE_CAPTURE_COALITION -- local Coalition = self:GetCoalition() -- self:E({Coalition = Coalition}) -- if Coalition == coalition.side.BLUE then - -- US_CC:MessageTypeToCoalition( string.format( "%s is under attack by Russia", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) - -- RU_CC:MessageTypeToCoalition( string.format( "We are attacking %s", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) + -- MESSAGE:New(string.format( "%s is under attack by Russia", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE) + -- MESSAGE:New(string.format( "We are attacking %s", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED) -- else - -- RU_CC:MessageTypeToCoalition( string.format( "%s is under attack by the USA", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) - -- US_CC:MessageTypeToCoalition( string.format( "We are attacking %s", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) + -- MESSAGE:New(string.format( "%s is under attack by the USA", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED) + -- MESSAGE:New(string.format( "We are attacking %s", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE) -- end -- end -- @@ -326,12 +296,12 @@ do -- ZONE_CAPTURE_COALITION -- local Coalition = self:GetCoalition() -- self:E({Coalition = Coalition}) -- if Coalition == coalition.side.BLUE then - -- RU_CC:MessageTypeToCoalition( string.format( "%s is captured by the USA, we lost it!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) - -- US_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) + -- MESSAGE:New(string.format( "%s is captured by the USA, we lost it!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED) + -- MESSAGE:New(string.format( "We captured %s, Excellent job!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE) -- else - -- US_CC:MessageTypeToCoalition( string.format( "%s is captured by Russia, we lost it!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) - -- RU_CC:MessageTypeToCoalition( string.format( "We captured %s, Excellent job!", ZoneCaptureCoalition:GetZoneName() ), MESSAGE.Type.Information ) - -- end + -- MESSAGE:New(string.format( "%s is captured by Russia, we lost it!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.BLUE) + -- MESSAGE:New(string.format( "We captured %s, Excellent job!", ZoneCaptureCoalition:GetZoneName() ),15,MESSAGE.Type.Information):ToCoalition(coalition.side.RED) + -- end -- -- self:__Guard( 30 ) -- end From 514ac30ad133c830fbe870827ae44c23fb6fd315 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 5 Mar 2026 16:01:42 +0100 Subject: [PATCH 3/7] #MSRS - Added "Speaker" option for HOUND/PIPER sub-voices --- Moose Development/Moose/Sound/SRS.lua | 33 ++++++++++++------- Moose Development/Moose/Sound/SoundOutput.lua | 14 +++++++- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index cd20b5033..ecede9691 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1536,7 +1536,7 @@ function MSRS:PlaySoundText(SoundText, Delay) if self.backend==MSRS.Backend.GRPC then self:_DCSgRPCtts(SoundText.text, nil, SoundText.gender, SoundText.culture, SoundText.voice, SoundText.volume, SoundText.label, SoundText.coordinate) elseif self.backend == MSRS.Backend.HOUND then - self:_HoundTextToSpeech(SoundText.text,nil,nil,SoundText.volume,SoundText.label,self.coalition,SoundText.coordinate,SoundText.Speed,SoundText.gender,SoundText.culture,SoundText.voice) + self:_HoundTextToSpeech(SoundText.text,nil,nil,SoundText.volume,SoundText.label,self.coalition,SoundText.coordinate,SoundText.Speed,SoundText.gender,SoundText.culture,SoundText.voice,nil,SoundText.speaker) else -- Get command. @@ -1561,21 +1561,22 @@ end -- @param #number Delay Delay in seconds, before the message is played. -- @param Core.Point#COORDINATE Coordinate Coordinate. -- @param #number Speed +-- @param #string Speaker Speaker (Sub-Voice) for PIPER only -- @return #MSRS self -function MSRS:PlayText(Text, Delay, Coordinate, Speed) +function MSRS:PlayText(Text, Delay, Coordinate, Speed, Speaker) self:F( {Text, Delay, Coordinate} ) if Delay and Delay>0 then - self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate) + self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate, Speed, Speaker) else if self.backend==MSRS.Backend.GRPC then self:T(self.lid.."Transmitting") self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate) elseif self.backend==MSRS.Backend.HOUND then - self:_HoundTextToSpeech(Text,nil,nil,nil,nil,nil,Coordinate,Speed) + self:_HoundTextToSpeech(Text,nil,nil,nil,nil,nil,Coordinate,Speed,nil,Speaker) else - self:PlayTextExt(Text, Delay, nil, nil, nil, nil, nil, nil, nil, Coordinate, Speed) + self:PlayTextExt(Text, Delay, nil, nil, nil, nil, nil, nil, nil, Coordinate, Speed, Speaker) end end @@ -1596,8 +1597,9 @@ end -- @param #string Label Label. -- @param Core.Point#COORDINATE Coordinate Coordinate. -- @param #number Speed Speed. +-- @param #string Speaker Speaker (Sub-Voice) for PIPER only -- @return #MSRS self -function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate,Speed) +function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate,Speed,Speaker) self:T({Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate, Speed} ) if Delay and Delay>0 then @@ -1628,7 +1630,7 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture local UseGoogle = (self.provider == MSRS.Provider.GOOGLE) and true or nil - self:_HoundTextToSpeech(Text,Frequencies,Modulations,Volume,Label,self.coalition,Coordinate,Speed,Gender,Culture,Voice,UseGoogle) + self:_HoundTextToSpeech(Text,Frequencies,Modulations,Volume,Label,self.coalition,Coordinate,Speed,Gender,Culture,Voice,UseGoogle,Speaker) end @@ -2029,8 +2031,9 @@ end -- @param #string Culture (Optional) Culture to use. -- @param #string Voice (Optional) Voice to use. -- @param #boolean UseGoogle (Optional) If to use Google TTS. +-- @param #string Speaker Speaker (Sub-Voice) for PIPER only -- @return SpeechTime Speech time in seconds. -function MSRS:_HoundTextToSpeech(Message,Frequencies,Modulations,Volume,Label,Coalition,Point,Speed,Gender,Culture,Voice,UseGoogle) +function MSRS:_HoundTextToSpeech(Message,Frequencies,Modulations,Volume,Label,Coalition,Point,Speed,Gender,Culture,Voice,UseGoogle,Speaker) self:I(self.lid.."_HoundTextToSpeech") Frequencies = UTILS.EnsureTable(Frequencies) @@ -2087,6 +2090,7 @@ function MSRS:_HoundTextToSpeech(Message,Frequencies,Modulations,Volume,Label,Co speed = speed, culture = culture, gender = gender, + speaker = Speaker, } local speechtime = HoundTTS.Transmit(Message, TransmissionP, ProviderP) @@ -2407,6 +2411,8 @@ MSRSQUEUE = { -- @field #number volume Volume -- @field #string label Label to be used -- @field Core.Point#COORDINATE coordinate Coordinate for this transmission +-- @field #number speed Speed of speech 1=100% +-- @field #string speaker PIPER subvoice "speaker" -- @field #number speed Speed to be used --- Create a new MSRSQUEUE object for a given radio frequency/modulation. @@ -2493,8 +2499,9 @@ end -- @param #string label (Optional) Label to be used -- @param Core.Point#COORDINATE coordinate (Optional) Coordinate to be used -- @param #number speed (Optional) Speed to be used +-- @param #string speaker (Optional) PIPER voice can have various speakers, set this here if you use PIPER/HOUND with a fitting voice. -- @return #MSRSQUEUE.Transmission Radio transmission table. -function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation, gender, culture, voice, volume, label,coordinate,speed) +function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation, gender, culture, voice, volume, label,coordinate,speed,speaker) self:T({Text=text, Dur=duration, start=tstart, int=interval, sub=subgroups, subt=subtitle, sudb=subduration, F=frequency, M=modulation, G=gender, C=culture, V=voice, Vol=volume, L=label, S=speed}) if self.TransmitOnlyWithPlayers then if self.PlayerSet and self.PlayerSet:CountAlive() == 0 then @@ -2536,7 +2543,9 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr transmission.label = label or msrs.Label transmission.coordinate = coordinate or msrs.coordinate transmission.speed = speed or 1.0 - + if speaker then + transmission.speaker = speaker + end -- Add transmission to queue. self:AddTransmission(transmission) @@ -2550,9 +2559,9 @@ function MSRSQUEUE:Broadcast(transmission) self:T(self.lid.."Broadcast") if transmission.frequency then - transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label, transmission.coordinate, transmission.speed) + transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label, transmission.coordinate, transmission.speed, transmission.speaker) else - transmission.msrs:PlayText(transmission.text,nil,transmission.coordinate,transmission.speed) + transmission.msrs:PlayText(transmission.text,nil,transmission.coordinate,transmission.speed,transmission.speaker) end local function texttogroup(gid) diff --git a/Moose Development/Moose/Sound/SoundOutput.lua b/Moose Development/Moose/Sound/SoundOutput.lua index aa54b895c..05546943f 100644 --- a/Moose Development/Moose/Sound/SoundOutput.lua +++ b/Moose Development/Moose/Sound/SoundOutput.lua @@ -301,6 +301,7 @@ do -- Text-To-Speech -- @field #string culture Culture, e.g. "en-GB". -- @field #string voice Specific voice to use. Overrules `gender` and `culture` settings. -- @field #number speed Specific speed to be used. + -- @field #string speaker (PIPER/HOUND only) sub-voice speaker to be used. -- @extends Core.Base#BASE @@ -427,7 +428,7 @@ do -- Text-To-Speech return self end - --- Set to use a specific speed. + --- Set to use a specific speed. -- @param #SOUNDTEXT self -- @param #number Speed -- @return #SOUNDTEXT self @@ -438,4 +439,15 @@ do -- Text-To-Speech return self end + --- Set to use a specific speaker (PIPER sub-voice). + -- @param #SOUNDTEXT self + -- @param #string Speaker + -- @return #SOUNDTEXT self + function SOUNDTEXT:SetSpeaker(Speaker) + + self.speaker = Speaker + + return self + end + end \ No newline at end of file From 2cedcff73c28a6d980f58aa82faaa20a5d73ebe7 Mon Sep 17 00:00:00 2001 From: leka1986 <83298840+leka1986@users.noreply.github.com> Date: Fri, 6 Mar 2026 20:16:21 +0100 Subject: [PATCH 4/7] Update CTLD.lua Removed C-130 from the Messaging Unloaded by ground crew. Scheduler for batch building is now 0 to make each set build it's own with it's engineer. He will despawn after 30 seconds after build. Shorter message for C-130 airdrop buildstarted. --- Moose Development/Moose/Ops/CTLD.lua | 65 ++++++++++++++++++---------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index cc95f9804..f49b982ca 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -200,7 +200,7 @@ do -- my_ctld.loadSavedCrates = true -- Load back crates (STATIC) from the save file. Useful for mission restart cleanup. (Default is true) -- my_ctld.UseC130LoadAndUnload = false -- When set to true, forces the C-130 player to use the C-130J built system to load the cargo onboard and to unload. (Default is false) -- my_ctld.UseC130DynamicCargoAutoBuild = false -- When true (and UseC130LoadAndUnload is true), C-130 DynamicCargo unload completion is bridged to CTLD engineer-path auto-build. --- my_ctld.C130DynamicCargoAutoBuildMergeSeconds = 10 -- Merge window in seconds for C-130 auto-build handoff; ready sets from same C-130 are batched into one engineer build call. +-- my_ctld.C130DynamicCargoAutoBuildMergeSeconds = 0 -- Merge window in seconds for C-130 auto-build handoff; set to 0 to disable batching (default). -- my_ctld.locale = "en" -- Language locale to use, available are "en" (default), "de" and "fr" -- -- ## 2.1 CH-47 Chinook support @@ -253,7 +253,7 @@ do -- After a valid airdrop and landing, CTLD automatically starts the build. -- -- If multiple compatible cargo sets are dropped close together, CTLD waits briefly --- (10 seconds by default) and then processes them together. +-- and then processes them together (C130DynamicCargoAutoBuildMergeSeconds; default 0 = no merge delay). -- -- ### Required settings -- @@ -722,7 +722,7 @@ CTLD = { pickupZones = {}, DynamicCargo = {}, UseC130DynamicCargoAutoBuild = false, - C130DynamicCargoAutoBuildMergeSeconds = 10, + C130DynamicCargoAutoBuildMergeSeconds = 0, ChinookTroopCircleRadius = 5, TroopUnloadDistGround = 5, TroopUnloadDistGroundHerc = 25, @@ -1096,7 +1096,7 @@ function CTLD:New(Coalition, Prefixes, Alias) self.UseC130DynamicCargoAutoBuild = false -- merge ready C-130 auto-build sets from the same aircraft for this many seconds. - self.C130DynamicCargoAutoBuildMergeSeconds = 10 + self.C130DynamicCargoAutoBuildMergeSeconds = 0 -- Smokes and Flares self.SmokeColor = SMOKECOLOR.Red @@ -2273,7 +2273,7 @@ function CTLD:_C130DcAutoQueueReadySet(SetId) if setData.completed or setData.buildStarted or setData.handoffClaimed then return true end local ownerKey = self:_C130DcAutoGetOwnerKey(setData) or SetId - local window = tonumber(self.C130DynamicCargoAutoBuildMergeSeconds) or 10 + local window = self.C130DynamicCargoAutoBuildMergeSeconds or 0 if window < 0 then window = 0 end @@ -2671,9 +2671,11 @@ function CTLD:_EventHandler(EventData) self.Loaded_Cargo[unitname] = loaded end local Group = client:GetGroup() + if not self:IsC130J(client, true) then local msg = self.gettext:GetEntry("CRATE_UNLOADED_GROUNDCREW",self.locale) msg = string.format(msg,event.IniDynamicCargoName) self:_SendMessage(msg, 10, false, Group) + end --self:_SendMessage(string.format("Crate %s unloaded by ground crew!",event.IniDynamicCargoName), 10, false, Group) self:__CratesDropped(1,Group,client,{dcargo}) self:_RefreshCrateQuantityMenus(Group, client, nil) @@ -5331,6 +5333,7 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop,NotifyGroup) end local crates,number = self:_FindCratesNearby(Group,Unit,finddist,true,true,not Engineering) -- #table local activeSetId = Engineering and self._c130DcAutoActiveSetId or nil + local isC130Auto = Engineering and activeSetId ~= nil local notifyGroup = (not Engineering) and Group or nil if activeSetId then crates, number = self:_C130DcAutoFilterCrates(crates, activeSetId) @@ -5417,6 +5420,7 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop,NotifyGroup) end -- end dropped end -- end crate loop -- ok let\'s list what we have + if not isC130Auto then local report = REPORT:New("Checklist Buildable Crates") report:Add("------------------------------------------------------------") for _,_build in pairs(buildables) do @@ -5446,9 +5450,32 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop,NotifyGroup) else self:T(text) end + end -- let\'s get going if canbuild then local notified=false + local function notifyBuildStarted(buildName, etaSeconds) + if notified then return end + local startMsgGroup = (not Engineering and (notifyGroup or Group)) or notifyGroup + if isC130Auto then + if startMsgGroup then + local msg + if etaSeconds and etaSeconds > 0 then + msg = string.format("CTLD: Building %s (ETA %ds).", tostring(buildName), math.floor(etaSeconds)) + else + msg = string.format("CTLD: Building %s.", tostring(buildName)) + end + self:_SendMessage(msg, 15, false, startMsgGroup) + end + else + local msg = self.gettext:GetEntry("BUILD_STARTED",self.locale) + msg = string.format(msg,self.buildtime) + if startMsgGroup then + self:_SendMessage(msg, 15, false, startMsgGroup) + end + end + notified=true + end -- loop again for _,_build in pairs(buildables) do local build = _build -- #CTLD.Buildable @@ -5473,16 +5500,13 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop,NotifyGroup) if self.buildtime and self.buildtime > 0 then local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate(),MultiDrop) buildtimer:Start(self.buildtime) - if not notified then - local msg = self.gettext:GetEntry("BUILD_STARTED",self.locale) - msg = string.format(msg,self.buildtime) - local startMsgGroup = (not Engineering and (notifyGroup or Group)) or notifyGroup - self:_SendMessage(msg, 15, false, startMsgGroup) - --self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) - notified=true - end + notifyBuildStarted(build.Name, self.buildtime) + self:__CratesBuildStarted(1,Group,Unit,build.Name) else + if isC130Auto then + notifyBuildStarted(build.Name, nil) + end self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop) end else @@ -5500,18 +5524,13 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop,NotifyGroup) if self.buildtime and self.buildtime > 0 then local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,b,false,Group:GetCoordinate(),MultiDrop) buildtimer:Start(self.buildtime) - if not notified then - local msg = self.gettext:GetEntry("BUILD_STARTED",self.locale) - msg = string.format(msg,self.buildtime) - local startMsgGroup = (not Engineering and (notifyGroup or Group)) or notifyGroup - if startMsgGroup then - self:_SendMessage(msg, 15, false, startMsgGroup) - end - --self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) - notified=true - end + notifyBuildStarted(build.Name, self.buildtime) + self:__CratesBuildStarted(1,Group,Unit,build.Name) else + if isC130Auto then + notifyBuildStarted(build.Name, nil) + end self:_BuildObjectFromCrates(Group,Unit,b,false,nil,MultiDrop) end end From 835b7d66a17143e8c064b40661e5dc38ba6dec0e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 7 Mar 2026 09:56:29 +0100 Subject: [PATCH 5/7] #CSAR - Fix setting correct provider if a provider is given and a google credentials path is there at the same time --- Moose Development/Moose/Ops/CSAR.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index c30547478..44bf10e53 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -2833,17 +2833,19 @@ function CSAR:onafterStart(From, Event, To) self.msrs = MSRS:New(path,channel,modulation) -- Sound.SRS#MSRS self.msrs:SetPort(self.SRSport) self.msrs:SetLabel("CSAR") - self.msrs:SetBackend(self.SRSBackend) - self.msrs:SetProvider(self.SRSProvider) + self.msrs:SetBackend(self.SRSBackend) self.msrs.speed = self.SRSSpeed self.msrs:SetCulture(self.SRSCulture) self.msrs:SetCoalition(self.coalition) self.msrs:SetVoice(self.SRSVoice) self.msrs:SetGender(self.SRSGender) - if self.SRSGPathToCredentials then + if self.SRSGPathToCredentials and (not self.SRSProvider) then self.msrs:SetProviderOptionsGoogle(self.SRSGPathToCredentials,self.SRSGPathToCredentials) self.msrs:SetProvider(MSRS.Provider.GOOGLE) end + if self.SRSProvider then + self.msrs:SetProvider(self.SRSProvider) + end self.msrs:SetVolume(self.SRSVolume) self.msrs:SetLabel("CSAR") self.SRSQueue = MSRSQUEUE:New("CSAR") -- Sound.SRS#MSRSQUEUE From dc8ebf7faac7208c1e35340225af7cc04f6f8e4c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 7 Mar 2026 11:35:12 +0100 Subject: [PATCH 6/7] #VARIOUS - Added provider options for TTS and updated logic --- Moose Development/Moose/Core/Message.lua | 9 +++++++-- Moose Development/Moose/Functional/AICSAR.lua | 8 ++++++-- Moose Development/Moose/Functional/Autolase.lua | 12 ++++++++---- Moose Development/Moose/Functional/Range.lua | 8 ++++++-- Moose Development/Moose/Ops/ATIS.lua | 8 ++++++-- Moose Development/Moose/Ops/Awacs.lua | 14 +++++++++++--- Moose Development/Moose/Ops/CSAR.lua | 2 +- Moose Development/Moose/Ops/FlightControl.lua | 9 ++++++++- Moose Development/Moose/Ops/OpsGroup.lua | 6 +++++- Moose Development/Moose/Ops/PlayerRecce.lua | 8 ++++++-- Moose Development/Moose/Ops/PlayerTask.lua | 10 +++++++--- Moose Development/Moose/Sound/SRS.lua | 11 ++++++----- 12 files changed, 77 insertions(+), 28 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 7492bb997..47cbf62c1 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -495,7 +495,8 @@ _MESSAGESRS = {} -- @param #number Volume (optional) Volume, can be between 0.0 and 1.0 (loudest). -- @param #string Label (optional) Label, defaults to "MESSAGE" or the Message Category set. -- @param Core.Point#COORDINATE Coordinate (optional) Coordinate this messages originates from. --- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC +-- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC or MSRS.Backend.HOUND etc +-- @param #string Provider (optional) Privider to be used, can be MSRS.Provider.WINDOWS or MSRS.Backend.GOOGLE or MSRS.Backend.PIPER etc -- @usage -- -- Mind the dot here, not using the colon this time around! -- -- Needed once only @@ -503,7 +504,7 @@ _MESSAGESRS = {} -- -- later on in your code -- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS() -- -function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate,Backend) +function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate,Backend,Provider) _MESSAGESRS.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio" @@ -536,6 +537,10 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G _MESSAGESRS.MSRS:SetProvider(MSRS.Provider.GOOGLE) end + if Provider then + _MESSAGESRS.MSRS:SetProvider(Provider) + end + _MESSAGESRS.label = Label or MSRS.Label or "MESSAGE" _MESSAGESRS.MSRS:SetLabel(_MESSAGESRS.label) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index 262366042..0f0a542d1 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -579,8 +579,9 @@ end -- @param #string Culture (Optional) The culture to be used, defaults to "en-GB" -- @param #string Gender (Optional) The gender to be used, defaults to "male" -- @param #string GoogleCredentials (Optional) Path to google credentials +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #AICSAR self -function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Culture,Gender,GoogleCredentials) +function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Culture,Gender,GoogleCredentials,Provider) self:T(self.lid .. "SetSRSTTSRadio") self.SRSTTSRadio = OnOff and true self.SRSRadio = false @@ -596,11 +597,14 @@ function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Cultur self.SRS:SetVoice(Voice) self.SRS:SetCulture(Culture) self.SRS:SetGender(Gender) - if GoogleCredentials then + if GoogleCredentials and not Provider then self.SRS:SetProviderOptionsGoogle(GoogleCredentials,GoogleCredentials) self.SRS:SetProvider(MSRS.Provider.GOOGLE) self.SRSGoogle = true end + if Provider then + self.SRS:SetProvider(Provider) + end self.SRSQ = MSRSQUEUE:New(self.alias) end return self diff --git a/Moose Development/Moose/Functional/Autolase.lua b/Moose Development/Moose/Functional/Autolase.lua index 8b909ef39..2efee7010 100644 --- a/Moose Development/Moose/Functional/Autolase.lua +++ b/Moose Development/Moose/Functional/Autolase.lua @@ -502,10 +502,11 @@ end -- @param #number Port (Optional) Defaults to 5002 -- @param #string Voice (Optional) Use a specifc voice with the @{Sound.SRS#SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. --- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) --- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS +-- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest). +-- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS. +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #AUTOLASE self -function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey) +function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Provider) if OnOff then self.useSRS = true self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio" @@ -528,10 +529,13 @@ function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Cultu self.SRS:SetVoice(self.Voice) self.SRS:SetCoalition(self.coalition) self.SRS:SetVolume(self.Volume) - if self.PathToGoogleKey then + if self.PathToGoogleKey and not Provider then self.SRS:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey) self.SRS:SetProvider(MSRS.Provider.GOOGLE) end + if Provider then + self.SRS:SetProvider(Provider) + end self.SRSQueue = MSRSQUEUE:New(self.alias) else self.useSRS = false diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 0ab0122c5..84b37cc0e 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1258,8 +1258,9 @@ end -- @param #number Modulation (Optional) Modulation to use, defaults to radio.modulation.AM -- @param #number Volume (Optional) Volume, between 0.0 and 1.0. Defaults to 1.0 -- @param #string PathToGoogleKey Path to Google TTS credentials. +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #RANGE self -function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey) +function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey,Provider) if PathToSRS or MSRS.path then @@ -1291,7 +1292,10 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, self.instructmsrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey) self.instructmsrs:SetProvider(MSRS.Provider.GOOGLE) end - + if Provider then + self.controlmsrs:SetProvider(Provider) + self.instructmsrs:SetProvider(Provider) + end else self:E(self.lid..string.format("ERROR: No SRS path specified!")) end diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index b42699971..251636e4b 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1672,9 +1672,10 @@ end -- @param #string Culture (Optional) Culture, e.g. "en-GB" (default). -- @param #string Voice (Optional) Specific voice. Overrides `Gender` and `Culture`. -- @param #number Port (Optional) SRS port. Default 5002. --- @param #string GoogleKey Path to Google JSON-Key (SRS exe backend) or Google API key (DCS-gRPC backend). +-- @param #string GoogleKey (Optional) Path to Google JSON-Key (SRS exe backend) or Google API key (DCS-gRPC backend). +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #ATIS self -function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) +function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey,Provider) --if PathToSRS or MSRS.path then self.useSRS=true @@ -1700,6 +1701,9 @@ function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) end self.msrs:SetVoice(voice) self.msrs:SetCoordinate(self.airbase:GetCoordinate()) + if Provider then + self.msrs:SetProvider(Provider) + end self.msrsQ = MSRSQUEUE:New("ATIS") self.msrsQ:SetTransmitOnlyWithPlayers(self.TransmitOnlyWithPlayers) if self.dTQueueCheck<=10 then diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 261ddfd16..e58b1b187 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1415,11 +1415,12 @@ end -- @param #number Modulation (Optional) Modulation to use, defaults to radio.modulation.AM. -- @param #number Interval Seconds between each update call. -- @param #number Number Number of Frequencies to create, can be 1..10. +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #AWACS self -function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number) +function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number,Provider) self:T(self.lid.."SetTacticalRadios") if not self.AwacsSRS then - MESSAGE:New("AWACS: Setup SRS in your code BEFORE trying to add tac radios please!",30,"ERROR",true):ToLog():ToAll() + MESSAGE:New("AWACS: Setup SRS in your code BEFORE trying to add tactical radios please!",30,"ERROR",true):ToLog():ToAll() return self end self.TacticalMenu = true @@ -1448,6 +1449,9 @@ function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number) self.TacticalSRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) self.TacticalSRS:SetProvider(MSRS.Provider.GOOGLE) end + if Provider then + self.TacticalSRS:SetProvider(Provider) + end self.TacticalSRSQ = MSRSQUEUE:New("Tactical AWACS") end return self @@ -2186,8 +2190,9 @@ end -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS; if you use a config file for MSRS, hand in nil here. -- @param #string AccessKey (Optional) Your Google API access key. This is necessary if DCS-gRPC is used as backend; if you use a config file for MSRS, hand in nil here. -- @param #string Backend (Optional) Your MSRS Backend if different from your config file settings, e.g. MSRS.Backend.SRSEXE or MSRS.Backend.GRPC +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #AWACS self -function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Backend) +function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Backend,Provider) self:T(self.lid.."SetSRS") self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio" self.Gender = Gender or MSRS.gender or "male" @@ -2210,6 +2215,9 @@ function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey --self.AwacsSRS:SetGoogle(self.PathToGoogleKey) self.AwacsSRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.AccessKey) self.AwacsSRS:SetProvider(MSRS.Provider.GOOGLE) + end + if Provider then + self.AwacsSRS:SetProvider(Provider) end -- Pre-configured Google? if (not PathToGoogleKey) and self.AwacsSRS:GetProvider() == MSRS.Provider.GOOGLE then diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 44bf10e53..e6bd21ddf 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -2839,7 +2839,7 @@ function CSAR:onafterStart(From, Event, To) self.msrs:SetCoalition(self.coalition) self.msrs:SetVoice(self.SRSVoice) self.msrs:SetGender(self.SRSGender) - if self.SRSGPathToCredentials and (not self.SRSProvider) then + if self.SRSGPathToCredentials then self.msrs:SetProviderOptionsGoogle(self.SRSGPathToCredentials,self.SRSGPathToCredentials) self.msrs:SetProvider(MSRS.Provider.GOOGLE) end diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index e4e5175b8..982eb55e6 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -364,8 +364,9 @@ FLIGHTCONTROL.version="0.7.7" -- @param #string PathToSRS (Optional) Path to the directory, where SRS is located. -- @param #number Port (Optional) Port of SRS Server, defaults to 5002 -- @param #string GoogleKey (Optional) Path to the Google JSON-Key. +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port, GoogleKey) +function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port, GoogleKey,Provider) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #FLIGHTCONTROL @@ -436,6 +437,9 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port, if GoogleKey then self.msrsTower:SetProviderOptionsGoogle(GoogleKey,GoogleKey) self.msrsTower:SetProvider(MSRS.Provider.GOOGLE) + end + if Provider then + self.msrsTower:SetProvider(Provider) end self.msrsTower:SetCoordinate(self:GetCoordinate()) self:SetSRSTower() @@ -446,6 +450,9 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port, if GoogleKey then self.msrsPilot:SetProviderOptionsGoogle(GoogleKey,GoogleKey) self.msrsPilot:SetProvider(MSRS.Provider.GOOGLE) + end + if Provider then + self.msrsPilot:SetProvider(Provider) end self.msrsTower:SetCoordinate(self:GetCoordinate()) self:SetSRSPilot() diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 2a483c54b..cf8e6defd 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -2355,8 +2355,9 @@ end -- @param #string PathToGoogleKey (Optional) Full path to the google credentials JSON file, e.g. `"C:\Users\myUsername\Downloads\key.json"`. -- @param #string Label (Optional) Label of the SRS comms for the SRS Radio overlay. Defaults to "ROBOT". No spaces allowed! -- @param #number Volume (Optional) Volume to be set, 0.0 = silent, 1.0 = loudest. Defaults to 1.0 +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #OPSGROUP self -function OPSGROUP:SetSRS(PathToSRS, Gender, Culture, Voice, Port, PathToGoogleKey, Label, Volume) +function OPSGROUP:SetSRS(PathToSRS, Gender, Culture, Voice, Port, PathToGoogleKey, Label, Volume,Provider) self.useSRS=true local path = PathToSRS or MSRS.path local port = Port or MSRS.port @@ -2370,6 +2371,9 @@ function OPSGROUP:SetSRS(PathToSRS, Gender, Culture, Voice, Port, PathToGoogleKe self.msrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey) self.msrs:SetProvider(MSRS.Provider.GOOGLE) end + if Provider then + self.msrs:SetProvider(Provider) + end self.msrs:SetCoalition(self:GetCoalition()) self.msrs:SetVolume(Volume) return self diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index 1356b4d90..c454f0699 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -1552,9 +1552,10 @@ end -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS --- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC +-- @param #string Backend (optional) Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC etc +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #PLAYERRECCE self -function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Backend) +function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Backend,Provider) self:T(self.lid.."SetSRS") self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio" -- self.Gender = Gender or MSRS.gender or "male" -- @@ -1582,6 +1583,9 @@ function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,V if self.PathToGoogleKey then self.SRS:SetProviderOptionsGoogle(self.PathToGoogleKey,self.PathToGoogleKey) self.SRS:SetProvider(MSRS.Provider.GOOGLE) + end + if Provider then + self.SRS:SetProvider(Provider) end -- Pre-configured Google? if (not PathToGoogleKey) and self.SRS:GetProvider() == MSRS.Provider.GOOGLE then diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 6348b0be7..9485cbab8 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -4908,11 +4908,12 @@ end -- Note that this must be installed on your windows system. Can also be Google voice types, if you are using Google TTS. -- @param #number Volume (Optional) Volume - between 0.0 (silent) and 1.0 (loudest) -- @param #string PathToGoogleKey (Optional) Path to your google key if you want to use google TTS; if you use a config file for MSRS, hand in nil here. --- @param #string AccessKey (Optional) Your Google API access key. This is necessary if DCS-gRPC is used as backend; if you use a config file for MSRS, hand in nil here. +-- @param #string AccessKey (Optional) Your Google API access key. This is necessary if DCS-gRPC is used as backend; if you use a config file for MSRS, you can hand in nil here. -- @param Core.Point#COORDINATE Coordinate (Optional) Coordinate from which the controller radio is sending --- @param #string Backend (Optional) MSRS Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC; if you use a config file for MSRS, hand in nil here. +-- @param #string Backend (Optional) MSRS Backend to be used, can be MSRS.Backend.SRSEXE or MSRS.Backend.GRPC; if you use a config file for MSRS, you can hand in nil here. +-- @param #string Provider (Optional) TTS Provider to be used. -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate,Backend) +function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate,Backend,Provider) self:T(self.lid.."SetSRS") self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio" -- self.Gender = Gender or MSRS.gender or "male" -- @@ -4946,6 +4947,9 @@ function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Cultu self.Voice = Voice or MSRS.poptions.gcloud.voice self.AccessKey = AccessKey or MSRS.poptions.gcloud.key end + if Provider then + self.SRS:SetProvider(Provider) + end if Coordinate then self.SRS:SetCoordinate(Coordinate) end diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index ecede9691..dd042cf2e 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -2034,7 +2034,7 @@ end -- @param #string Speaker Speaker (Sub-Voice) for PIPER only -- @return SpeechTime Speech time in seconds. function MSRS:_HoundTextToSpeech(Message,Frequencies,Modulations,Volume,Label,Coalition,Point,Speed,Gender,Culture,Voice,UseGoogle,Speaker) - self:I(self.lid.."_HoundTextToSpeech") + self:T(self.lid.."_HoundTextToSpeech") Frequencies = UTILS.EnsureTable(Frequencies) Modulations = UTILS.EnsureTable(Modulations) @@ -2065,7 +2065,7 @@ function MSRS:_HoundTextToSpeech(Message,Frequencies,Modulations,Volume,Label,Co modus=modus:gsub("0", "AM") modus=modus:gsub("1", "FM") - self:I({T=Message,F=freqs,M=modus,V=voice,Vx=volume,L=label,C=coal,GGL=tostring(UseGoogle)}) + self:T({T=Message,F=freqs,M=modus,V=voice,Vx=volume,L=label,C=coal,GGL=tostring(UseGoogle)}) if (UseGoogle ~= true) and self.provider == MSRS.Provider.GOOGLE then UseGoogle = true @@ -2130,8 +2130,8 @@ end -- | speed | number | `1.0` | Speech rate (0.5 = half speed, 1.0 = normal, 2.0 = double speed) | -- function MSRS:_HoundTransmit(Message, Transmission_params, Provider_params) - self:I(self.lid.."_HoundTransmit") - self:I({Message,Transmission_params,Provider_params}) + self:T(self.lid.."_HoundTransmit") + self:T({Message,Transmission_params,Provider_params}) local speechtime = HoundTTS.Transmit(Message, Transmission_params, Provider_params) return speechtime end @@ -2143,7 +2143,7 @@ end -- @param #table Modulations The table of modulations to use. -- @param #number Coalition The coalition to use. function MSRS:_HoundTestTone(Frequencies, Modulations, Coalition) - self:I(self.lid.."_HoundTestTone") + self:T(self.lid.."_HoundTestTone") Frequencies = UTILS.EnsureTable(Frequencies) Modulations = UTILS.EnsureTable(Modulations) @@ -2169,6 +2169,7 @@ end -- @param #number Speed (Optional) The speed to use, defaults to 1.0. -- @param #boolean UseGoogle (Optional) If to use google. Default: no. function MSRS:_HoundSpeechTime(Message,Speed,UseGoogle) + self:T(self.lid.."_HoundSpeechTime") local speed = Speed or 1.0 local speechtime = HoundTTS.getSpeechTime(Message, speed, UseGoogle) return speechtime From 94179bbb33278933b9865aa268f2f88cdc55de8c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 7 Mar 2026 12:05:27 +0100 Subject: [PATCH 7/7] #MSRS - Speaker options for Piper --- Moose Development/Moose/Sound/SRS.lua | 49 ++++++++++++++++++--------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index dd042cf2e..28105c7ab 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1197,6 +1197,17 @@ function MSRS:SetVoicePiper(Voice) return self end +--- Set to use a specific speaker for a voice if Piper is used as provider (only Hound-TTS backend). +-- @param #MSRS self +-- @param #string Speaker (Optional) [Piper Voices](https://rhasspy.github.io/piper-samples/). Some have speakers as sub-voices. +-- @return #MSRS self +function MSRS:SetSpeakerPiper(Speaker) + self:F( {Speaker=Speaker} ) + self.Speaker = Speaker + + 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 (Optional) [Azure Voice](https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). Default `"en-US-AriaNeural"`. @@ -1570,14 +1581,16 @@ function MSRS:PlayText(Text, Delay, Coordinate, Speed, Speaker) self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate, Speed, Speaker) else - if self.backend==MSRS.Backend.GRPC then - self:T(self.lid.."Transmitting") - self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate) - elseif self.backend==MSRS.Backend.HOUND then - self:_HoundTextToSpeech(Text,nil,nil,nil,nil,nil,Coordinate,Speed,nil,Speaker) - else - self:PlayTextExt(Text, Delay, nil, nil, nil, nil, nil, nil, nil, Coordinate, Speed, Speaker) - end + local speaker = Speaker or self.Speaker + + if self.backend==MSRS.Backend.GRPC then + self:T(self.lid.."Transmitting") + self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate) + elseif self.backend==MSRS.Backend.HOUND then + self:_HoundTextToSpeech(Text,nil,nil,nil,nil,nil,Coordinate,Speed,nil,speaker) + else + self:PlayTextExt(Text, Delay, nil, nil, nil, nil, nil, nil, nil, Coordinate, Speed, speaker) + end end @@ -1600,10 +1613,10 @@ end -- @param #string Speaker Speaker (Sub-Voice) for PIPER only -- @return #MSRS self function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate,Speed,Speaker) - self:T({Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate, Speed} ) + self:T({Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate, Speed, Speaker} ) if Delay and Delay>0 then - self:ScheduleOnce(Delay, self.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate, Speed) + self:ScheduleOnce(Delay, self.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate, Speed, Speaker) else Frequencies = Frequencies or self:GetFrequencies() @@ -1628,9 +1641,11 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture elseif self.backend==MSRS.Backend.HOUND then -- BASE:I("MSRS.Backend.HOUND") + local speaker = Speaker or self.Speaker + local UseGoogle = (self.provider == MSRS.Provider.GOOGLE) and true or nil - self:_HoundTextToSpeech(Text,Frequencies,Modulations,Volume,Label,self.coalition,Coordinate,Speed,Gender,Culture,Voice,UseGoogle,Speaker) + self:_HoundTextToSpeech(Text,Frequencies,Modulations,Volume,Label,self.coalition,Coordinate,Speed,Gender,Culture,Voice,UseGoogle,speaker) end @@ -2067,9 +2082,9 @@ function MSRS:_HoundTextToSpeech(Message,Frequencies,Modulations,Volume,Label,Co self:T({T=Message,F=freqs,M=modus,V=voice,Vx=volume,L=label,C=coal,GGL=tostring(UseGoogle)}) - if (UseGoogle ~= true) and self.provider == MSRS.Provider.GOOGLE then - UseGoogle = true - end + --if (UseGoogle ~= true) and self.provider == MSRS.Provider.GOOGLE then + --UseGoogle = true + --end local provider = self.provider provider=provider:gsub("gcloud", "google") @@ -2090,7 +2105,7 @@ function MSRS:_HoundTextToSpeech(Message,Frequencies,Modulations,Volume,Label,Co speed = speed, culture = culture, gender = gender, - speaker = Speaker, + speaker = Speaker or self.Speaker, } local speechtime = HoundTTS.Transmit(Message, TransmissionP, ProviderP) @@ -2545,7 +2560,9 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr transmission.coordinate = coordinate or msrs.coordinate transmission.speed = speed or 1.0 if speaker then - transmission.speaker = speaker + transmission.speaker = speaker + elseif msrs.Speaker then + transmission.speaker = msrs.speaker end -- Add transmission to queue. self:AddTransmission(transmission)