Sound update

This commit is contained in:
Frank 2021-05-28 23:00:43 +02:00
parent 3129c8c8ea
commit 4827b73bb1
4 changed files with 373 additions and 69 deletions

View File

@ -59,7 +59,7 @@
-- @field #number frequency Radio frequency in MHz.
-- @field #number modulation Radio modulation 0=AM or 1=FM.
-- @field #number power Radio power in Watts. Default 100 W.
-- @field Core.RadioQueue#RADIOQUEUE radioqueue Radio queue for broadcasing messages.
-- @field Sound.RadioQueue#RADIOQUEUE radioqueue Radio queue for broadcasing messages.
-- @field #string soundpath Path to sound files.
-- @field #string relayunitname Name of the radio relay unit.
-- @field #table towerfrequency Table with tower frequencies.
@ -2102,6 +2102,14 @@ end
-- @param #string Text Report text.
function ATIS:onafterReport(From, Event, To, Text)
self:T(self.lid..string.format("Report:\n%s", Text))
-- Remove line breaks
local text=string.gsub(Text, "[\r\n]", "")
env.info("FF: "..text)
local msrs=MSRS:New("D:\\DCS\\_SRS\\", 305, Modulation)
msrs:PlayText(text)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -35,6 +35,7 @@
-- @field #table numbers Table of number transmission parameters.
-- @field #boolean checking Scheduler is checking the radio queue.
-- @field #boolean schedonce Call ScheduleOnce instead of normal scheduler.
-- @field Sound.SRS#MSRS msrs Moose SRS class.
-- @extends Core.Base#BASE
RADIOQUEUE = {
ClassName = "RADIOQUEUE",
@ -69,6 +70,8 @@ RADIOQUEUE = {
-- @field #boolean isplaying If true, transmission is currently playing.
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
-- @field #number interval Interval in seconds before next transmission.
-- @field Sound.SoundFile#SOUNDFILE soundfile Sound file object to play via SRS.
-- @field Sound.SoundFile#SOUNDTEXT soundtext Sound TTS object to play via SRS.
--- Create a new RADIOQUEUE object for a given radio frequency/modulation.
@ -170,6 +173,15 @@ function RADIOQUEUE:SetRadioPower(power)
return self
end
--- Set SRS.
-- @param #RADIOQUEUE self
-- @param #string PathToSRS Path to SRS.
-- @return #RADIOQUEUE self The RADIOQUEUE object.
function RADIOQUEUE:SetSRS(PathToSRS)
self.msrs=MSRS:New(PathToSRS, self.frequency/1000000, self.modulation)
return self
end
--- Set parameters of a digit.
-- @param #RADIOQUEUE self
-- @param #number digit The digit 0-9.
@ -230,7 +242,7 @@ end
-- @param #number interval Interval in seconds after the last transmission finished.
-- @param #string subtitle Subtitle of the transmission.
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
-- @return #RADIOQUEUE self The RADIOQUEUE object.
-- @return #RADIOQUEUE.Transmission Radio transmission table.
function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval, subtitle, subduration)
env.info("FF new transmission.")
@ -271,7 +283,7 @@ function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval,
-- Add transmission to queue.
self:AddTransmission(transmission)
return self
return transmission
end
--- Create a new transmission and add it to the radio queue.
@ -280,10 +292,10 @@ end
-- @param #number tstart Start time (abs) seconds. Default now.
-- @param #number interval Interval in seconds after the last transmission finished.
-- @return #RADIOQUEUE self
function RADIOQUEUE:AddSoundfile(soundfile, tstart, interval)
function RADIOQUEUE:AddSoundFile(soundfile, tstart, interval)
env.info(string.format("FF add soundfile: name=%s%s", soundfile:GetPath(), soundfile:GetFileName()))
self:NewTransmission(soundfile:GetFileName(), soundfile.duration, soundfile:GetPath(), tstart, interval, soundfile.subtitle, soundfile.subduration)
local transmission=self:NewTransmission(soundfile:GetFileName(), soundfile.duration, soundfile:GetPath(), tstart, interval, soundfile.subtitle, soundfile.subduration)
transmission.soundfile=soundfile
return self
end
@ -295,19 +307,9 @@ end
-- @param #number interval Interval between the next call.
-- @return #number Duration of the call in seconds.
function RADIOQUEUE:Number2Transmission(number, delay, interval)
--- Split string into characters.
local function _split(str)
local chars={}
for i=1,#str do
local c=str:sub(i,i)
table.insert(chars, c)
end
return chars
end
-- Split string into characters.
local numbers=UTILS.GetCharacters(number) --l_split(number)
local numbers=UTILS.GetCharacters(number)
local wait=0
for i=1,#numbers do
@ -340,6 +342,11 @@ end
-- @param #RADIOQUEUE.Transmission transmission The transmission.
function RADIOQUEUE:Broadcast(transmission)
if (transmission.soundfile or transmission.soundtext) and self.msrs then
self:_BroadcastSRS(transmission)
return
end
-- Get unit sending the transmission.
local sender=self:_GetRadioSender()
@ -431,6 +438,19 @@ function RADIOQUEUE:Broadcast(transmission)
end
end
--- Broadcast radio message.
-- @param #RADIOQUEUE self
-- @param #RADIOQUEUE.Transmission transmission The transmission.
function RADIOQUEUE:_BroadcastSRS(transmission)
if transmission.soundfile then
self.msrs:PlaySoundFile(transmission.soundfile)
elseif transmission.soundtext then
self.msrs:PlaySoundText(transmission.soundtext)
end
end
--- Start checking the radio queue.
-- @param #RADIOQUEUE self
-- @param #number delay Delay in seconds before checking.

View File

@ -1,4 +1,4 @@
--- **Sound** - Simple Radio Standalone Integration
--- **Sound** - Simple Radio Standalone (SRS) Integration.
--
-- ===
--
@ -41,7 +41,10 @@
-- @field #string name Name. Default "DCS-STTS".
-- @field #number volume Volume between 0 (min) and 1 (max). Default 1.
-- @field #string culture Culture. Default "en-GB".
-- @field #string path Path to the SRS exe.
-- @field #string gender Gender. Default "female".
-- @field #string voice Specifc voce.
-- @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 "/".
-- @extends Core.Base#BASE
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
@ -62,16 +65,17 @@
MSRS = {
ClassName = "MSRS",
lid = nil,
port = 5002,
name = "MSRS",
frequencies = {},
modulations = {},
coalition = 0,
speed = 1,
port = 5002,
name = "DCS-STTS",
volume = 1,
culture = "en-GB",
gender = "female",
culture = "en-GB",
voice = nil,
volume = 1,
speed = 1,
coordinate = nil,
latitude = nil,
longitude = nil,
altitude = nil,
@ -79,7 +83,7 @@ MSRS = {
--- MSRS class version.
-- @field #string version
MSRS.version="0.0.1"
MSRS.version="0.0.2"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -94,8 +98,8 @@ MSRS.version="0.0.1"
--- Create a new MSRS object.
-- @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.
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators.
-- @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.
-- @return #MSRS self
function MSRS:New(PathToSRS, Frequency, Modulation)
@ -107,9 +111,11 @@ function MSRS:New(PathToSRS, Frequency, Modulation)
local self=BASE:Inherit(self, BASE:New()) -- #MSRS
self:SetPath(PathToSRS)
self:SetPort()
self:SetFrequencies(Frequency)
self:SetModulations(Modulation)
self:SetGender()
return self
end
@ -117,7 +123,6 @@ end
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- 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.
@ -125,6 +130,7 @@ end
function MSRS:SetPath(Path)
if Path==nil then
self:E("ERROR: No path to SRS directory specified!")
return nil
end
@ -138,6 +144,8 @@ function MSRS:SetPath(Path)
n=n+1
end
self.path=self.path.."/"
self:I(string.format("SRS path=%s", self:GetPath()))
return self
@ -145,11 +153,26 @@ end
--- Get path to SRS directory.
-- @param #MSRS self
-- @return #string Path to the directory.
-- @return #string Path to the directory. This includes the final slash "/".
function MSRS:GetPath()
return self.path
end
--- Set port.
-- @param #MSRS self
-- @param #number Port Port. Default 5002.
-- @return #MSRS self
function MSRS:SetPort(Port)
self.port=Port or 5002
end
--- Get port.
-- @param #MSRS self
-- @return #number Port.
function MSRS:GetPort()
return self.port
end
--- Set frequencies.
-- @param #MSRS self
-- @param #table Frequencies Frequencies in MHz. Can also be given as a #number if only one frequency should be used.
@ -166,6 +189,13 @@ function MSRS:SetFrequencies(Frequencies)
return self
end
--- Get frequencies.
-- @param #MSRS self
-- @param #table Frequencies in MHz.
function MSRS:GetFrequencies()
return self.frequencies
end
--- Set modulations.
-- @param #MSRS self
@ -183,25 +213,73 @@ function MSRS:SetModulations(Modulations)
return self
end
--- Get modulations.
-- @param #MSRS self
-- @param #table Modulations.
function MSRS:GetModulations()
return self.modulations
end
--- Set gender.
-- @param #MSRS self
-- @param #string Gender Gender: "male" or "female" (default).
-- @return #MSRS self
function MSRS:SetGender(Gender)
Gender=Gender or "female"
Gender=Gender:lower()
self.gender=Gender
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Transmission Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Play sound file (ogg or mp3) via SRS.
-- @param #MSRS self
-- @param Sound.SoundFile#SOUNDFILE Soundfile Sound file to play.
-- @param #number Delay Delay in seconds, before the sound file is played.
-- @return #MSRS self
function MSRS:PlaySoundfile(Soundfile, Delay)
function MSRS:PlaySoundFile(Soundfile, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MSRS.PlaySoundfile, self, Soundfile, 0)
self:ScheduleOnce(Delay, MSRS.PlaySoundFile, self, Soundfile, 0)
else
local exe=self:GetPath().."/".."DCS-SR-ExternalAudio.exe"
local soundfile=Soundfile:GetName()
local freq=table.concat(self.frequencies, " ")
local modu=table.concat(self.modulations, " ")
local coal=self.coalition
local port=self.port
local command=self:_GetCommand()
local command=string.format("%s --file %s --freqs %s --modulations %s --coalition %d --port %d -h", exe, soundfile, freq, modu, coal, port)
command=command.." --file="..tostring(soundfile)
env.info(string.format("FF PlaySoundfile command=%s", command))
-- Execute SRS command.
os.execute(command)
end
return self
end
--- Play a SOUNDTEXT text-to-speech object.
-- @param #MSRS self
-- @param Sound.SoundFile#SOUNDTEXT SoundText Sound text.
-- @param #number Delay Delay in seconds, before the sound file is played.
-- @return #MSRS self
function MSRS:PlaySoundText(SoundText, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MSRS.PlaySoundText, self, SoundText, 0)
else
local command=self:_GetCommand(nil, nil, nil, SoundText.gender, SoundText.voice, SoundText.culture, SoundText.volume, SoundText.speed)
command=command..string.format(" --text=\"%s\"", tostring(SoundText.text))
env.info(string.format("FF PlaySoundfile command=%s", command))
@ -215,29 +293,26 @@ end
--- Play text message via STTS.
-- @param #MSRS self
-- @param #string Message Text message.
-- @param #string Text Text message.
-- @param #number Delay Delay in seconds, before the message is played.
-- @return #MSRS self
function MSRS:PlayText(Message, Delay)
function MSRS:PlayText(Text, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, MSRS.PlayText, self, Message, 0)
self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, 0)
else
local text=string.format("\"%s\"", Message)
local exe=self:GetPath().."/".."DCS-SR-ExternalAudio.exe"
local freq=table.concat(self.frequencies, " ")
local modu=table.concat(self.modulations, " ")
local coal=self.coalition
local port=self.port
local gender="male"
local text=string.format("\"%s\"", Text)
local command=string.format("%s -h --text=%s --freqs=%s --modulations=%s --coalition=%d --port=%d --gender=%s", exe, text, freq, modu, coal, port, gender)
local command=self:_GetCommand()
command=command..string.format(" --text=\"%s\"", tostring(Text))
env.info(string.format("FF Text command=%s", command))
-- Execute SRS command.
os.execute(command)
local x=os.execute(command)
env.info(x)
end
@ -249,6 +324,46 @@ end
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- 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.
-- @return #string Command.
function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port)
local exe=self:GetPath().."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
local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --gender=%s --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, gender, volume, speed)
if voice then
command=command..string.format(" --voice=\"%s\"", tostring(voice))
end
if culture then
command=command.." --culture="..tostring(culture)
end
env.info("FF command="..command)
return command
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
--- **Sound** - Sound file management.
--- **Sound** - Sound output classes.
--
-- ===
--
@ -16,12 +16,46 @@
-- @image Sound_Soundfile.png
--
do -- Sound Base
--- @type SOUNDBASE
-- @field #string ClassName Name of the class
-- @extends Core.Base#BASE
--- Sound files used by other classes.
--
-- # 1. USERFLAG constructor
--
-- * @{#USERFLAG.New}(): Creates a new USERFLAG object.
--
-- @field #SOUNDBASE
SOUNDBASE={
ClassName = "SOUNDBASE",
}
--- Constructor to create a new SOUNDBASE object.
-- @param #SOUNDBASE self
-- @return #SOUNDBASE self
function SOUNDBASE:New()
-- Inherit BASE.
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDFILE
return self
end
end
do -- Sound File
--- @type SOUNDFILE
-- @field #string ClassName Name of the class
-- @field #string filename Name of the flag.
-- @field #string path Directory path, where the sound file is located.
-- @field #string path Directory path, where the sound file is located. This includes the final slash "/".
-- @field #string duration Duration of the sound file in seconds.
-- @field #string subtitle Subtitle of the transmission.
-- @field #number subduration Duration in seconds how long the subtitle is displayed.
@ -39,7 +73,7 @@ do -- Sound File
SOUNDFILE={
ClassName = "SOUNDFILE",
filename = nil,
path = "l10n/DEFAULT",
path = "l10n/DEFAULT/",
duration = 3,
subtitle = nil,
subduration = 0,
@ -58,12 +92,10 @@ do -- Sound File
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDFILE
-- Set file name.
self.filename=FileName or "Hallo World.ogg"
--TODO: check that sound file is .ogg or .mp3
self:SetFileName(FileName)
-- Set path
self.path=Path or "l10n/DEFAULT/"
self:SetPath(Path)
self.duration=Duration or 3
@ -79,19 +111,39 @@ do -- Sound File
-- @return #SOUNDFILE self
function SOUNDFILE:SetPath(Path)
-- Init path.
self.path=Path or "l10n/DEFAULT/"
-- Remove (back)slashes.
local nmax=1000
local n=1
local nmax=1000 ; local n=1
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
-- Append slash.
self.path=self.path.."/"
return self
end
--- Get path of the directory, where the sound file is located.
-- @param #SOUNDFILE self
-- @return #string Path.
function SOUNDFILE:GetPath()
local path=self.path or "l10n/DEFAULT/"
return path
end
--- Set sound file name. This must be a .ogg or .mp3 file!
-- @param #SOUNDFILE self
-- @param #string FileName Name of the file.
-- @return #SOUNDFILE self
function SOUNDFILE:SetFileName(FileName)
--TODO: check that sound file is really .ogg or .mp3
self.filename=FileName or "HelloWorld.mp3"
return self
end
--- Get the sound file name.
-- @param #SOUNDFILE self
@ -99,24 +151,133 @@ do -- Sound File
function SOUNDFILE:GetFileName()
return self.filename
end
--- Get path of the directory, where the sound file is located.
--- Set duration how long it takes to play the sound file.
-- @param #SOUNDFILE self
-- @return #string Path.
function SOUNDFILE:GetPath()
local path=self.path or "l10n/DEFAULT"
path=path.."/"
return path
-- @param #string Duration Duration in seconds. Default 3 seconds.
-- @return #SOUNDFILE self
function SOUNDFILE:SetDuration(Duration)
self.duration=Duration or 3
return self
end
--- Get duration how long the sound file takes to play.
-- @param #SOUNDFILE self
-- @return #number Duration in seconds.
function SOUNDFILE:GetDuration()
return self.duration or 3
end
--- Get the complete sound file name inlcuding its path.
-- @param #SOUNDFILE self
-- @return #string Name of the sound file.
function SOUNDFILE:GetName()
local filename=self:GetFileName()
local path=self:GetPath()
local name=string.format("%s/%s", path, filename)
local filename=self:GetFileName()
local name=string.format("%s%s", path, filename)
return name
end
end
do -- Text-To-Speech
--- @type SOUNDTEXT
-- @field #string ClassName Name of the class
-- @field #string text Text to speak.
-- @field #number duration Duration in seconds.
-- @field #string gender Gender.
-- @field #string voice Voice.
-- @field #string culture Culture.
-- @extends Core.Base#BASE
--- Sound files used by other classes.
--
-- # 1. USERFLAG constructor
--
-- * @{#USERFLAG.New}(): Creates a new USERFLAG object.
--
-- @field #SOUNDTEXT
SOUNDTEXT={
ClassName = "SOUNDTEXT",
}
--- Constructor to create a new SOUNDTEXT object.
-- @param #SOUNDTEXT self
-- @param #string Text The text to speak.
-- @param #number Duration Duration in seconds, how long it takes to play the text. Default is 3 seconds.
-- @return #SOUNDTEXT self
function SOUNDTEXT:New(Text, Duration)
-- Inherit BASE.
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDTEXT
self:SetText(Text)
self:SetDuration(Duration)
self:SetGender()
self:SetCulture()
-- Debug info:
self:I(string.format("New SOUNDTEXT: text=%s, duration=%.1f sec", self.text, self.duration))
return self
end
--- Set text.
-- @param #SOUNDTEXT self
-- @param #string Text Text to speak. Default "Hello World!".
-- @return #SOUNDTEXT self
function SOUNDTEXT:SetText(Text)
self.text=Text or "Hello World!"
return self
end
--- Set duration, how long it takes to speak the text.
-- @param #SOUNDTEXT self
-- @param #number Duration Duration in seconds. Default 3 seconds.
-- @return #SOUNDTEXT self
function SOUNDTEXT:SetDuration(Duration)
self.duration=Duration or 3
return self
end
--- Set gender.
-- @param #SOUNDTEXT self
-- @param #string Gender Gender: "male" or "female" (default).
-- @return #SOUNDTEXT self
function SOUNDTEXT:SetGender(Gender)
self.gender=Gender or "female"
return self
end
--- Set the voice name. See the list from --help or if using google see: https://cloud.google.com/text-to-speech/docs/voices
-- @param #SOUNDTEXT self
-- @param #string Voice
-- @return #SOUNDTEXT self
function SOUNDTEXT:SetVoice(Voice)
self.voice=Voice
return self
end
--- Set TTS culture - local for the voice.
-- @param #SOUNDTEXT self
-- @param #string Culture TTS culture. Default "en-GB".
-- @return #SOUNDTEXT self
function SOUNDTEXT:SetCulture(Culture)
self.culture=Culture or "en-GB"
return self
end
end