module("VOICE")
local localize_keys = {
on = m_i18n_voice("on %s"),
from = m_i18n_voice("from %s"),
to = m_i18n_voice("to %s"),
route_summary_simple = m_i18n_voice("Route summary."),
route_summary_detailed = m_i18n_voice("Route summary."),
tollgate_simple = m_i18n_voice("Toll booth ahead."),
tollgate_detailed = m_i18n_voice("%s toll booth ahead.")
}
local removeDotFromEndOfTheSentence = function(wSentence)
local dotPattern = L"%.%s*$"
if wstring.find(wSentence, dotPattern) then
wSentence = wstring.gsub(wSentence, dotPattern, L"")
end
return wSentence
end
local addDotToEndOfTheSentence = function(wSentence)
local dotPattern = L"%.%s*$"
if not wstring.find(wSentence, dotPattern) then
wSentence = wSentence .. L"."
end
return wSentence
end
dVoiceFormat_search_distance = function(inputdistance)
local unit_names = {"yards","meters","feet"}
local voicetable_name = type(VOICE.distances["ttspro_"..unit_names[MODEL.regional.units() + 1]]) == "table" and "ttspro_" or ""
local dist_table = VOICE.distances[voicetable_name..unit_names[MODEL.regional.units() + 1]]
if voicetable_name == "ttspro_" and inputdistance > dist_table[#dist_table-1] or #dist_table == 0 then
dist_table = VOICE.distances[unit_names[MODEL.regional.units() + 1]]
end
local _,_,outputdistance = VOICE.distance_formatter(inputdistance)
if #dist_table == 0 or inputdistance > dist_table[#dist_table-1] then
return builtin.format(L"%{distance:3}",outputdistance)
end
local ind = dVoiceFormat_search_index(outputdistance,dist_table)
if outputdistance ~= dist_table[ind] then
ind = dVoiceFormat_search_index(inputdistance,dist_table)
end
return VOICE.decode_string(dist_table[ind + 1], 1)
end
dVoiceFormat_search_index = function(distance, dist_table)
if distance <= dist_table[1] then
return 1
elseif distance >= dist_table[#dist_table-1] then
return #dist_table-1
else
local lo = 1
local hi = #dist_table >> 1
local dist, idx
repeat
idx = (lo+hi)>>1
dist = dist_table[idx<<1 - 1]
if dist < distance then
lo = idx
elseif dist > distance then
hi = idx
else
break
end
until (hi-lo < 2)
if (dist < distance) then idx = idx+1 end
return idx<<1 - 1
end
end
local function speed_camera(param_table)
local distanceSentence,dist = L"",L""
local say_dist = SysConfig:get("warning","say_camera_distance",0)
if param_table.speedcamera_distance and say_dist then
dist = param_table.speedcamera_distance
dist = dist - (MODEL.navigation.car.current_speed() * sc_GetSysEntry("tts", "reaction_time_ms", 1500) / 100 / 36)
distanceSentence = translated_voice_format(m_i18n_voice("After %s"), dVoiceFormat_search_distance(dist))
end
distanceSentence = removeDotFromEndOfTheSentence(distanceSentence)
local localizedDescKey, typ = get_localizationToDescKey(param_table)
if localizedDescKey == L"" then
param_table.descKey = "Speed camera ahead."
voice_debug_log("The localization is missing. Fallback to \'Speed camera ahead.\'", 2)
localizedDescKey, typ = get_localizationToDescKey(param_table)
end
localizedDescKey = removeDotFromEndOfTheSentence(localizedDescKey)
if localizedDescKey == L"" then
return localizedDescKey
end
local speedCameraSentence = L""
if distanceSentence ~= L"" and wstring.find(distanceSentence, L"%%s") then
speedCameraSentence = wstring.gsub(distanceSentence, L"%%s", localizedDescKey)
else
speedCameraSentence = localizedDescKey
end
if param_table.speedcamera_limit then
local speedCameraSpeedLimitSentence = over_speed_limit_new(param_table.speedcamera_limit)
if speedCameraSpeedLimitSentence ~= L"" then
speedCameraSentence = speedCameraSentence .. L" " .. speedCameraSpeedLimitSentence
end
end
speedCameraSentence = addDotToEndOfTheSentence(speedCameraSentence)
speedCameraSentence = distanceSentence .. L" " .. speedCameraSentence
return wstring.gsub(speedCameraSentence, distanceSentence .. L" " .. translate_voice(m_i18n_voice("Reduce your speed.")), translate_voice(m_i18n_voice("Reduce your speed.")))
end
local get_localizationFromLang = function(descKey)
if MODEL.regional.is_it_localizable(descKey) then
return translate(m_i18n(descKey))
end
voice_debug_log(wstring.format(L"The needed localization to the \'%s\' descKey is missing from the lang!", towstring(descKey)), 1)
return false
end
local get_localizationFromVoice = function(descKey)
if MODEL.regional.is_it_voice_localizable(descKey) then
return translate_voice(m_i18n_voice(descKey))
end
voice_debug_log(wstring.format(L"The needed localization to the \'%s\' descKey is missing from the voice!", towstring(descKey)), 1)
return false
end
function get_localizationToDescKey(param_table)
local paramTable = {}
if type(param_table) == "string" then
paramTable.desc_key = param_table
elseif type(param_table) == "table" then
paramTable = param_table
end
if paramTable.localized_sentence then
return paramTable.localized_sentence, "got"
end
if not paramTable.desc_key then
return L"", false
end
local localizedDescKey = get_localizationFromVoice(paramTable.desc_key)
if localizedDescKey then
return localizedDescKey, "voice"
end
localizedDescKey = get_localizationFromLang(paramTable.desc_key)
if localizedDescKey then
return localizedDescKey, "lang"
end
if announceDescKey and paramTable.desc_key then
voice_debug_log("The description key is announced, because of the localization is missing!", 3)
return towstring("For test only! " .. paramTable.desc_key), "devel"
end
voice_debug_log(string.format("Localization of the \'%s\' description key of the event is failed!", paramTable.desc_key), 3)
return L"", false
end
local common_localizer = function(param_table)
local localizedDescKey, typ = get_localizationToDescKey(param_table)
return localizedDescKey
end
local function get_localizerFunction(param_table)
if string.find(param_table.media_id, "^voice.iws.speedcam") then
return speed_camera
elseif string.find(param_table.media_id, "^voice.iws.overspeedlimit") then
return over_speed_limit
else
return common_localizer
end
end
function localize_sentence(param_table)
voice_debug_log("######## EVENT VOICE GUIDANCE ############################################################################", 3)
voice_debug_log_table(param_table)
local localizerFunction = get_localizerFunction(param_table)
local sentence
if string.find(param_table.media_id, "^voice.iws.overspeedlimit") then
sentence = localizerFunction()
else
sentence = localizerFunction(param_table)
end
voice_debug_log(wstring.format(L"Event sentence: \'%s\'", sentence), 3)
return sentence
end
local cityDistrictKeys = {
city = "You have entered CityName.",
district = "You have entered DistrictName.",
city_district = "You have entered CityName, DistrictName."
}
function city_or_district_changed_supported()
if MODEL.regional.is_it_voice_localizable(cityDistrictKeys.city) and MODEL.regional.is_it_voice_localizable(cityDistrictKeys.district) and MODEL.regional.is_it_voice_localizable(cityDistrictKeys.city_district) then
return true
else
return false
end
end
function city_or_district_changed(data)
voice_debug_log("## City or district changed ###############################################", 3)
voice_debug_log_table(data)
local retStr = L""
local localised_key = L""
if data.city ~= nil and data.district == nil then
ASSERT(MODEL.regional.is_it_voice_localizable(cityDistrictKeys.city), "Missing city-changed dictionary.voice key:" .. cityDistrictKeys.city)
localised_key = translate_voice(m_i18n_voice(cityDistrictKeys.city))
retStr = city_changed(data, localised_key)
elseif data.city == nil and data.district ~= nil then
ASSERT(MODEL.regional.is_it_voice_localizable(cityDistrictKeys.district), "Missing district-changed dictionary.voice key:" .. cityDistrictKeys.district)
localised_key = translate_voice(m_i18n_voice(cityDistrictKeys.district))
retStr = district_changed(data, localised_key)
elseif data.city ~= nil and data.district ~= nil then
retStr = city_and_district_changed(data)
else
voice_debug_log(" # Untreated type is in the data table! Neither city or district!", 1)
ASSERT(false)
end
voice_debug_log(L"## City and/or district changed, say: \'" .. retStr .. L"\' ##", 3)
return retStr
end
function city_changed(data, localised_key)
local cityName = L""
local retStr = L""
if data.city.phoneme then
cityName = data.city.phoneme
retStr = wstring.gsub(localised_key, L"CityName", cityName)
elseif data.city.text then
cityName = data.city.text
retStr = wstring.gsub(localised_key, L"CityName", cityName)
else
voice_debug_log(" # Untreated data type! Not text and phoneme!", 1)
ASSERT(false)
end
voice_debug_log(wstring.format(L"\tRaw-pattern: \'%s\' raw-city: \'%s\'", localised_key, cityName))
return retStr
end
function district_changed(data, localised_key)
local districtName = L""
local retStr = L""
if data.district.phoneme then
districtName = data.district.phoneme
retStr = wstring.gsub(localised_key, L"DistrictName", districtName)
elseif data.district.text then
districtName = data.district.text
retStr = wstring.gsub(localised_key, L"DistrictName", districtName)
else
voice_debug_log(" # Untreated data type! Not text and phoneme!", 1)
ASSERT(false)
end
voice_debug_log(wstring.format(L"\tRaw-pattern: \'%s\' raw-district: \'%s\'", localised_key, districtName))
return retStr
end
function city_and_district_changed(data)
local cityDistrictName = L""
local retStr = L""
local localised_key = L""
if wstring.find(data.district.text, data.city.text) then
ASSERT(MODEL.regional.is_it_voice_localizable(cityDistrictKeys.district), "Missing district-changed dictionary.voice key:" .. cityDistrictKeys.district)
localised_key = translate_voice(m_i18n_voice(cityDistrictKeys.district))
if data.district.phoneme then
cityDistrictName = data.district.phoneme
retStr = wstring.gsub(localised_key, L"DistrictName", cityDistrictName)
elseif data.district.text then
cityDistrictName = data.district.text
retStr = wstring.gsub(localised_key, L"DistrictName", cityDistrictName)
end
else
ASSERT(MODEL.regional.is_it_voice_localizable(cityDistrictKeys.city_district), "Missing city-district-changed dictionary.voice key:" .. cityDistrictKeys.city_district)
localised_key = translate_voice(m_i18n_voice(cityDistrictKeys.city_district))
localised_key = city_changed(data, localised_key)
retStr = district_changed(data, localised_key)
end
voice_debug_log(wstring.format(L"\tRaw-pattern: \'%s\' raw-city-district: \'%s\'", localised_key, cityDistrictName))
return retStr
end
local vics_other_keys = {
vics_ahead = m_i18n_voice("vics_ahead"),
vics_dist_1km = m_i18n_voice("vics_dist_1km"),
vics_dist_2km = m_i18n_voice("vics_dist_2km"),
vics_dist_3km = m_i18n_voice("vics_dist_3km")
}
function vics_event_supported()
if MODEL.regional.is_it_voice_localizable(vics_other_keys.vics_ahead) and MODEL.regional.is_it_voice_localizable(vics_other_keys.vics_dist_1km) and MODEL.regional.is_it_voice_localizable(vics_other_keys.vics_dist_2km) and MODEL.regional.is_it_voice_localizable(vics_other_keys.vics_dist_3km) then
return true
else
return false
end
end
function vics_event(descKey, data)
voice_debug_log("######## VICS EVENT VOICE GUIDANCE ############################################################################", 3)
voice_debug_log(string.format("VICS Event: \'%s\' ", descKey), 3)
voice_debug_log(string.format("VICS Event distance: \'%d\'", data.dist), 3)
local vicsSentence = L""
local localizedDescKey = translate_voice(descKey)
if #localizedDescKey == 0 then
voice_debug_log(string.format("There is no localization to the vics event \'%s\' in the voice!", descKey), 3)
return vicsSentence
end
if data.dist < 500 then
vicsSentence = wstring.gsub(translate_voice(vics_other_keys.vics_ahead), L"%%s", localizedDescKey)
elseif data.dist < 1500 then
vicsSentence = wstring.gsub(translate_voice(vics_other_keys.vics_dist_1km), L"%%s", localizedDescKey)
elseif data.dist < 2500 then
vicsSentence = wstring.gsub(translate_voice(vics_other_keys.vics_dist_2km), L"%%s", localizedDescKey)
elseif data.dist < 3500 then
vicsSentence = wstring.gsub(translate_voice(vics_other_keys.vics_dist_3km), L"%%s", localizedDescKey)
elseif data.dist == 3500 then
vicsSentence = wstring.gsub(translate_voice(vics_other_keys.vics_dist_3km), L"%%s", localizedDescKey)
else
voice_debug_log("The distance to the vics event is more than 3km! The announcement is not required!", 3)
return vicsSentence
end
voice_debug_log(wstring.format(L"VICS event sentence: \'%s\'", vicsSentence), 3)
return vicsSentence
end
function traffic_event_supported()
if MODEL.regional.is_it_voice_localizable(localize_keys.on) and MODEL.regional.is_it_voice_localizable(localize_keys.from) and MODEL.regional.is_it_voice_localizable(localize_keys.to) then
return true
else
return false
end
end
function traffic_event(DescKey, data)
voice_debug_log("------------------------------------------------------------------------------")
voice_debug_log("Traffic Event: " .. DescKey)
voice_debug_log("Traffic Event Data:")
voice_debug_log_table(data)
local str = translate_voice(DescKey)
local t = {}
if #str == 0 then
voice_debug_log("There is not localization in the voice to the event: " .. DescKey, 3)
return L""
end
table.insert(t, str)
if data.roadnumber then
local s = wstring.gsub(translate_voice(localize_keys.on), L"%%s", transform_and_format(data.roadnumber))
table.insert(t, s)
elseif data.roadname then
local s = wstring.gsub(translate_voice(localize_keys.on), L"%%s", transform_and_format(data.roadname))
table.insert(t, s)
else
voice_debug_log("There is not TMC data!", 3)
return L""
end
if data.from then
local s = wstring.gsub(translate_voice(localize_keys.from), L"%%s", transform_and_format(data.from))
table.insert(t, s)
end
if data.to then
local s = wstring.gsub(translate_voice(localize_keys.to), L"%%s", transform_and_format(data.to))
table.insert(t, s)
end
local tmcSentence = table_concat(t, L"; ")
voice_debug_log(L"Traffic event sentence: " .. tmcSentence, 3)
return tmcSentence
end
local MinTunnelBridgeLength = 200
function format_summary_part(data, typ)
if data then
local res = data.text
if typ == 0 then
res = route_summary_format_road_name(data)
elseif typ == 1 then
res = route_summary_format_street_name(data)
elseif typ == 2 then
res = route_summary_format_bridge_tunnel(data)
elseif typ == 3 then
res = route_summary_format_order(data)
end
return res
else
return L""
end
end
function route_summary_supported()
if route_summary_format_road_name and route_summary_format_street_name and route_summary_format_bridge_tunnel and route_summary_format_order and MODEL.regional.is_it_voice_localizable(localize_keys.route_summary_simple) and MODEL.regional.is_it_voice_localizable(localize_keys.route_summary_detailed) then
return true
else
return false
end
end
function update_next_waypoint(waypoints, curr_waypoint_info)
local next_waypoint_dist, next_waypoint_ordername
while curr_waypoint_info.index < #waypoints do
curr_waypoint_info.index = curr_waypoint_info.index + 1
next_waypoint_dist = waypoints[curr_waypoint_info.index].start_distance
next_waypoint_ordername = format_summary_part(waypoints[curr_waypoint_info.index].order_name, 3)
if next_waypoint_dist > curr_waypoint_info.dist and next_waypoint_ordername ~= L"" and next_waypoint_ordername ~= curr_waypoint_info.ordername then
curr_waypoint_info.ordername = next_waypoint_ordername
curr_waypoint_info.dist = next_waypoint_dist
return
end
end
curr_waypoint_info.index = #waypoints + 1
curr_waypoint_info.ordername = L""
curr_waypoint_info.dist = -1
end
function route_summary(summary, waypoints)
voice_debug_log("Route Summary Data:")
voice_debug_log_table(summary)
voice_debug_log("Route Summary waypoints:")
voice_debug_log_table(waypoints)
local route_summary_phrases_separator = L" . "
if type(get_route_summary_phrases_separator) == "function" then
route_summary_phrases_separator = get_route_summary_phrases_separator()
end
if #summary == 0 or #waypoints < 2 then
return L""
end
local furthest_end_dist = -1
local end_dist
local last_bridge_length = -1
local last_bridge_end = -1
local length, start_dist, roadname, ordername
local road_names = {}
for i = 1, #summary do
if summary[i].road_name then
roadname = tostring(summary[i].road_name.text)
if road_names[roadname] == nil then
road_names[roadname] = 1
else
road_names[roadname] = road_names[roadname] + 1
end
end
end
local i = 1
while i <= #summary do
length = summary[i].length
roadname = summary[i].road_name and summary[i].road_name.text or L""
ordername = summary[i].order_name and summary[i].order_name.text or L""
start_dist = summary[i].start_distance
if summary[i].typ == 0 and wstring.find(roadname, L"%d+") and route_summary_skip_numbers then
voice_debug_log(L"Route Summary item is removed, because of that is a number: " .. roadname, 3)
table.remove(summary, i)
elseif summary[i].typ == 2 then
if last_bridge_end ~= -1 and last_bridge_end ~= start_dist then
last_bridge_end = -1
last_bridge_length = -1
end
if length < MinTunnelBridgeLength then
table.remove(summary, i)
elseif roadname ~= L"" and 1 < road_names[tostring(roadname)] then
table.remove(summary, i)
elseif last_bridge_length ~= -1 then
if length <= last_bridge_length then
table.remove(summary, i)
else
table.remove(summary, i - 1)
last_bridge_length = length
last_bridge_end = start_dist + length
end
else
i = i + 1
last_bridge_length = length
last_bridge_end = start_dist + length
end
else
last_bridge_length = -1
last_bridge_end = -1
end_dist = start_dist + length
if furthest_end_dist >= end_dist then
table.remove(summary, i)
else
i = i + 1
furthest_end_dist = end_dist
end
end
end
local length = #summary
local lastordername = format_summary_part(waypoints[1].order_name, 3)
local lastroadname = L""
local last_phrase = L""
local phrases = {}
local next_waypoint_info = {
index = 1,
ordername = lastordername,
dist = waypoints[1].start_distance
}
update_next_waypoint(waypoints, next_waypoint_info)
for i = 1, length do
roadname = format_summary_part(summary[i].road_name, summary[i].typ)
ordername = format_summary_part(summary[i].order_name, 3)
start_dist = summary[i].start_distance
while next_waypoint_info.dist ~= -1 and start_dist >= next_waypoint_info.dist do
if next_waypoint_info.ordername ~= lastordername then
table.insert(phrases, next_waypoint_info.ordername)
last_phrase = next_waypoint_info.ordername
lastordername = next_waypoint_info.ordername
end
update_next_waypoint(waypoints, next_waypoint_info)
end
if ordername ~= L"" and ordername ~= lastordername and ordername ~= last_phrase then
table.insert(phrases, ordername)
last_phrase = ordername
end
if roadname ~= L"" and roadname ~= lastroadname and roadname ~= last_phrase then
table.insert(phrases, roadname)
last_phrase = roadname
end
if ordername ~= L"" then
lastordername = ordername
end
if roadname ~= L"" then
lastroadname = roadname
end
end
while next_waypoint_info.dist ~= -1 do
if next_waypoint_info.ordername ~= lastordername then
table.insert(phrases, next_waypoint_info.ordername)
lastordername = next_waypoint_info.ordername
end
update_next_waypoint(waypoints, next_waypoint_info)
end
local returnStr = translate_voice(localize_keys.route_summary_detailed) .. L" " .. table_concat(phrases, route_summary_phrases_separator)
voice_debug_log(returnStr)
return returnStr
end
local over_speed_limit_keys = {
key = "The speed limit is %s!",
kmph_pattern = L"(%d+)%s*km%/?h",
kmph = "kmph",
mph_pattern = L"(%d+)%s*mp?%/?h",
mph = "mph"
}
function over_speed_limit()
local params = {
}
if MODEL.regional.is_it_voice_localizable(over_speed_limit_keys.key) then
local unitformat
if announceSpeedUnit then
unitformat = ESpeedDisplayPart.Full
else
unitformat = ESpeedDisplayPart.Number
end
local SpeedLimit
if #params > 0 then
SpeedLimit = params[1]
else
SpeedLimit = MODEL.warning.driveralert.speed_limit()
end
if SpeedLimit == 0 then
voice_debug_log("The speed limit value is 0!", 2)
return L""
end
local SpeedLimitStr = Format_Speed(SpeedLimit, ESpeedDisplayMode.Rounded, unitformat)
local overspeedlimit = translated_voice_format(over_speed_limit_keys.key, SpeedLimitStr)
if wstring.find(overspeedlimit, over_speed_limit_keys.kmph_pattern) and MODEL.regional.is_it_voice_localizable(over_speed_limit_keys.kmph) then
overspeedlimit = wstring.gsub(overspeedlimit, over_speed_limit_keys.kmph_pattern, translate_voice(over_speed_limit_keys.kmph))
elseif wstring.find(overspeedlimit, over_speed_limit_keys.mph_pattern) and MODEL.regional.is_it_voice_localizable(over_speed_limit_keys.mph) then
overspeedlimit = wstring.gsub(overspeedlimit, over_speed_limit_keys.mph_pattern, translate_voice(over_speed_limit_keys.mph))
end
voice_debug_log(wstring.format(L"Over speed limit sentence: \'%s\'", overspeedlimit), 3)
return overspeedlimit
end
voice_debug_log(string.format("The needed localization to the \'%s\' descKey is missing from the voice!", over_speed_limit_keys.key), 1)
return L""
end
function over_speed_limit_new()
if MODEL.regional.is_it_voice_localizable(over_speed_limit_keys.key) then
local unitformat
if announceSpeedUnit then
unitformat = ESpeedDisplayPart.Full
else
unitformat = ESpeedDisplayPart.Number
end
local SpeedLimit
SpeedLimit = MODEL.navigation.curr_speedcam.speed_limit()
local SpeedLimitStr = Format_Speed(SpeedLimit, ESpeedDisplayMode.Rounded, unitformat)
local overspeedlimit = translated_voice_format(over_speed_limit_keys.key, SpeedLimitStr)
if wstring.find(overspeedlimit, over_speed_limit_keys.kmph_pattern) and MODEL.regional.is_it_voice_localizable(over_speed_limit_keys.kmph) then
overspeedlimit = wstring.gsub(overspeedlimit, over_speed_limit_keys.kmph_pattern, translate_voice(over_speed_limit_keys.kmph))
elseif wstring.find(overspeedlimit, over_speed_limit_keys.mph_pattern) and MODEL.regional.is_it_voice_localizable(over_speed_limit_keys.mph) then
overspeedlimit = wstring.gsub(overspeedlimit, over_speed_limit_keys.mph_pattern, translate_voice(over_speed_limit_keys.mph))
end
return overspeedlimit
end
voice_debug_log(string.format("The needed localization to the \'%s\' descKey is missing from the voice!", over_speed_limit_keys.key), 1)
return L""
end
secDirIconState = false
function secondaryDirIconChanged(value)
secDirIconState = value
end
function tollgate_supported()
if announce_tollgate and type(tollgate) == "function" and (MODEL.regional.is_it_voice_localizable(localize_keys.tollgate_simple) or MODEL.regional.is_it_voice_localizable(localize_keys.tollgate_detailed)) then
return true
else
return false
end
end
function tollgate(tollgateName)
if not tollgate_supported() then
return
end
if type(tollgateName) ~= "wstring" then
return
end
local tollgateWStr
if #tollgateName and MODEL.regional.is_it_voice_localizable(localize_keys.tollgate_detailed) then
tollgateWStr, _ = wstring.gsub(translate_voice(localize_keys.tollgate_detailed), L"%%s", tollgateName)
else
if not MODEL.regional.is_it_voice_localizable(localize_keys.tollgate_simple) then
voice_debug_log("Tolgate localization keys missing from dictionary.voice.")
return
end
tollgateWStr = translate_voice(localize_keys.tollgate_simple)
end
voice_debug_log(L"Tolgate: " .. tollgateWStr)
return tollgateWStr
end
Bookmarks