feat: Voice spoofing

- Added `voices` parameter, which takes a list maps for each voice to add. Example:
  `[ {"isLocalService": true, "isDefault": true, "voiceUri": "Ting-Ting", "name": "Ting-Ting", "lang": "zh-CN" } ... ]`
- Added `voices:blockIfNotDefined` has been added to block system voices
- Added `voices:fakeCompletion: bool` and `voices:fakeCompletion:charsPerSecond: double` to set a fake playback speed.
This commit is contained in:
daijro 2024-11-04 02:52:10 -06:00
parent 30001a4507
commit 74d016e9a9
3 changed files with 120 additions and 0 deletions

View file

@ -283,4 +283,30 @@ inline std::optional<std::array<int32_t, 3UL>> MShaderData(
return std::nullopt;
}
inline std::optional<
std::vector<std::tuple<std::string, std::string, std::string, bool, bool>>>
MVoices() {
auto data = GetJson();
if (!data.contains("voices") || !data["voices"].is_array()) {
return std::nullopt;
}
std::vector<std::tuple<std::string, std::string, std::string, bool, bool>>
voices;
for (const auto& voice : data["voices"]) {
// Check if voice has all required fields
if (!voice.contains("lang") || !voice.contains("name") ||
!voice.contains("voiceUri") || !voice.contains("isDefault") ||
!voice.contains("isLocalService")) {
continue;
}
voices.emplace_back(
voice["lang"].get<std::string>(), voice["name"].get<std::string>(),
voice["voiceUri"].get<std::string>(), voice["isDefault"].get<bool>(),
voice["isLocalService"].get<bool>());
}
return voices;
}
} // namespace MaskConfig

View file

@ -0,0 +1,90 @@
diff --git a/dom/media/webspeech/synth/moz.build b/dom/media/webspeech/synth/moz.build
index 2cf19982b2..dcdcdb5cbf 100644
--- a/dom/media/webspeech/synth/moz.build
+++ b/dom/media/webspeech/synth/moz.build
@@ -63,3 +63,6 @@ FINAL_LIBRARY = "xul"
LOCAL_INCLUDES += [
"ipc",
]
+
+# DOM Mask
+LOCAL_INCLUDES += ['/camoucfg']
\ No newline at end of file
diff --git a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
index e5a1353d6b..4a4a5b080b 100644
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
@@ -27,6 +27,7 @@
#include "SpeechSynthesisChild.h"
#include "SpeechSynthesisParent.h"
+#include "MaskConfig.hpp"
using mozilla::intl::LocaleService;
@@ -169,6 +170,20 @@ nsSynthVoiceRegistry* nsSynthVoiceRegistry::GetInstance() {
// Start up all speech synth services.
NS_CreateServicesFromCategory(NS_SPEECH_SYNTH_STARTED, nullptr,
NS_SPEECH_SYNTH_STARTED);
+ // Load voices from MaskConfig
+ if (auto voices = MaskConfig::MVoices()) {
+ for (const auto& [lang, name, uri, isDefault, isLocal] :
+ voices.value()) {
+ gSynthVoiceRegistry->AddVoiceImpl(
+ nullptr, NS_ConvertUTF8toUTF16(uri), NS_ConvertUTF8toUTF16(name),
+ NS_ConvertUTF8toUTF16(lang), isLocal,
+ false); // queuesUtterances set to false
+ if (isDefault) {
+ gSynthVoiceRegistry->SetDefaultVoice(NS_ConvertUTF8toUTF16(uri),
+ true);
+ }
+ }
+ }
}
}
@@ -305,6 +320,8 @@ nsSynthVoiceRegistry::AddVoice(nsISpeechService* aService,
return NS_ERROR_NOT_AVAILABLE;
}
+ if (MaskConfig::GetBool("voices:blockIfNotDefined")) return NS_OK;
+
return AddVoiceImpl(aService, aUri, aName, aLang, aLocalService,
aQueuesUtterances);
}
@@ -779,6 +796,35 @@ void nsSynthVoiceRegistry::SpeakImpl(VoiceData* aVoice, nsSpeechTask* aTask,
NS_ConvertUTF16toUTF8(aText).get(),
NS_ConvertUTF16toUTF8(aVoice->mUri).get(), aRate, aPitch));
+ // Check if this voice is in our MVoices config
+ if (auto voices = MaskConfig::MVoices()) {
+ for (const auto& [lang, name, uri, isDefault, isLocal] : voices.value()) {
+ if (NS_ConvertUTF8toUTF16(uri).Equals(aVoice->mUri)) {
+ printf_stderr("Tried to speak a fake voice: %s",
+ NS_ConvertUTF16toUTF8(aVoice->mUri).get());
+ aTask->Init();
+ // If fake completion is disabled, throw an error
+ if (!MaskConfig::GetBool("voices:fakeCompletion")) {
+ aTask->DispatchError(0, 0);
+ return;
+ }
+ float charsPerSecond;
+ if (auto value =
+ MaskConfig::GetDouble("voices:fakeCompletion:charsPerSecond")) {
+ charsPerSecond = value.value();
+ } else {
+ charsPerSecond = 12.5f;
+ }
+ // Return a fake success with a speach rate of 150wpm
+ aTask->DispatchStart();
+ float fakeElapsedTime =
+ static_cast<float>(aText.Length()) / (charsPerSecond * aRate);
+ aTask->DispatchEnd(fakeElapsedTime, aText.Length());
+ return;
+ }
+ }
+ }
+
aTask->Init();
if (NS_FAILED(aVoice->mService->Speak(aText, aVoice->mUri, aVolume, aRate,

View file

@ -83,6 +83,10 @@
{ "property": "webGl2:shaderPrecisionFormats:blockIfNotDefined", "type": "bool" },
{ "property": "webGl:contextAttributes", "type": "dict" },
{ "property": "webGl2:contextAttributes", "type": "dict" },
{ "property": "voices", "type": "array" },
{ "property": "voices:blockIfNotDefined", "type": "bool" },
{ "property": "voices:fakeCompletion", "type": "bool" },
{ "property": "voices:fakeCompletion:charsPerSecond", "type": "double" },
{ "property": "memorysaver", "type": "bool" },
{ "property": "debug", "type": "bool" }
]