tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsFakeSynthServices.cpp (7617B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsFakeSynthServices.h"
      8 
      9 #include "SharedBuffer.h"
     10 #include "mozilla/ClearOnShutdown.h"
     11 #include "mozilla/Preferences.h"
     12 #include "mozilla/dom/nsSpeechTask.h"
     13 #include "mozilla/dom/nsSynthVoiceRegistry.h"
     14 #include "nsISupports.h"
     15 #include "nsPrintfCString.h"
     16 #include "nsThreadUtils.h"
     17 #include "nsXULAppAPI.h"
     18 #include "prenv.h"
     19 
     20 #define CHANNELS 1
     21 #define SAMPLERATE 1600
     22 
     23 namespace mozilla::dom {
     24 
     25 StaticRefPtr<nsFakeSynthServices> nsFakeSynthServices::sSingleton;
     26 
     27 enum VoiceFlags {
     28  eSuppressEvents = 1,
     29  eSuppressEnd = 2,
     30  eFailAtStart = 4,
     31  eFail = 8
     32 };
     33 
     34 struct VoiceDetails {
     35  const char* uri;
     36  const char* name;
     37  const char* lang;
     38  bool defaultVoice;
     39  uint32_t flags;
     40 };
     41 
     42 static const VoiceDetails sVoices[] = {
     43    {"urn:moz-tts:fake:bob", "Bob Marley", "en-JM", true, 0},
     44    {"urn:moz-tts:fake:amy", "Amy Winehouse", "en-GB", false, 0},
     45    {"urn:moz-tts:fake:lenny", "Leonard Cohen", "en-CA", false, 0},
     46    {"urn:moz-tts:fake:celine", "Celine Dion", "fr-CA", false, 0},
     47    {
     48        "urn:moz-tts:fake:julie",
     49        "Julieta Venegas",
     50        "es-MX",
     51        false,
     52    },
     53    {"urn:moz-tts:fake:zanetta", "Zanetta Farussi", "it-IT", false, 0},
     54    {"urn:moz-tts:fake:margherita", "Margherita Durastanti",
     55     "it-IT-noevents-noend", false, eSuppressEvents | eSuppressEnd},
     56    {"urn:moz-tts:fake:teresa", "Teresa Cornelys", "it-IT-noend", false,
     57     eSuppressEnd},
     58    {"urn:moz-tts:fake:cecilia", "Cecilia Bartoli", "it-IT-failatstart", false,
     59     eFailAtStart},
     60    {"urn:moz-tts:fake:gottardo", "Gottardo Aldighieri", "it-IT-fail", false,
     61     eFail},
     62 };
     63 
     64 // FakeSynthCallback
     65 class FakeSynthCallback : public nsISpeechTaskCallback {
     66 public:
     67  explicit FakeSynthCallback(nsISpeechTask* aTask) : mTask(aTask) {}
     68  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     69  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(FakeSynthCallback,
     70                                           nsISpeechTaskCallback)
     71 
     72  NS_IMETHOD OnPause() override {
     73    if (mTask) {
     74      mTask->DispatchPause(1.5, 1);
     75    }
     76 
     77    return NS_OK;
     78  }
     79 
     80  NS_IMETHOD OnResume() override {
     81    if (mTask) {
     82      mTask->DispatchResume(1.5, 1);
     83    }
     84 
     85    return NS_OK;
     86  }
     87 
     88  NS_IMETHOD OnCancel() override {
     89    if (mTask) {
     90      mTask->DispatchEnd(1.5, 1);
     91    }
     92 
     93    return NS_OK;
     94  }
     95 
     96  NS_IMETHOD OnVolumeChanged(float aVolume) override { return NS_OK; }
     97 
     98 private:
     99  virtual ~FakeSynthCallback() = default;
    100 
    101  nsCOMPtr<nsISpeechTask> mTask;
    102 };
    103 
    104 NS_IMPL_CYCLE_COLLECTION(FakeSynthCallback, mTask);
    105 
    106 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FakeSynthCallback)
    107  NS_INTERFACE_MAP_ENTRY(nsISpeechTaskCallback)
    108  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTaskCallback)
    109 NS_INTERFACE_MAP_END
    110 
    111 NS_IMPL_CYCLE_COLLECTING_ADDREF(FakeSynthCallback)
    112 NS_IMPL_CYCLE_COLLECTING_RELEASE(FakeSynthCallback)
    113 
    114 // FakeSpeechSynth
    115 
    116 class FakeSpeechSynth : public nsISpeechService {
    117 public:
    118  FakeSpeechSynth() = default;
    119 
    120  NS_DECL_ISUPPORTS
    121  NS_DECL_NSISPEECHSERVICE
    122 
    123 private:
    124  virtual ~FakeSpeechSynth() = default;
    125 };
    126 
    127 NS_IMPL_ISUPPORTS(FakeSpeechSynth, nsISpeechService)
    128 
    129 NS_IMETHODIMP
    130 FakeSpeechSynth::Speak(const nsAString& aText, const nsAString& aUri,
    131                       float aVolume, float aRate, float aPitch,
    132                       nsISpeechTask* aTask) {
    133  class DispatchStart final : public Runnable {
    134   public:
    135    explicit DispatchStart(nsISpeechTask* aTask)
    136        : mozilla::Runnable("DispatchStart"), mTask(aTask) {}
    137 
    138    NS_IMETHOD Run() override {
    139      mTask->DispatchStart();
    140 
    141      return NS_OK;
    142    }
    143 
    144   private:
    145    nsCOMPtr<nsISpeechTask> mTask;
    146  };
    147 
    148  class DispatchEnd final : public Runnable {
    149   public:
    150    DispatchEnd(nsISpeechTask* aTask, const nsAString& aText)
    151        : mozilla::Runnable("DispatchEnd"), mTask(aTask), mText(aText) {}
    152 
    153    NS_IMETHOD Run() override {
    154      mTask->DispatchEnd(mText.Length() / 2, mText.Length());
    155 
    156      return NS_OK;
    157    }
    158 
    159   private:
    160    nsCOMPtr<nsISpeechTask> mTask;
    161    nsString mText;
    162  };
    163 
    164  class DispatchError final : public Runnable {
    165   public:
    166    DispatchError(nsISpeechTask* aTask, const nsAString& aText)
    167        : mozilla::Runnable("DispatchError"), mTask(aTask), mText(aText) {}
    168 
    169    NS_IMETHOD Run() override {
    170      mTask->DispatchError(mText.Length() / 2, mText.Length());
    171 
    172      return NS_OK;
    173    }
    174 
    175   private:
    176    nsCOMPtr<nsISpeechTask> mTask;
    177    nsString mText;
    178  };
    179 
    180  uint32_t flags = 0;
    181  for (VoiceDetails voice : sVoices) {
    182    if (aUri.EqualsASCII(voice.uri)) {
    183      flags = voice.flags;
    184      break;
    185    }
    186  }
    187 
    188  if (flags & eFailAtStart) {
    189    return NS_ERROR_FAILURE;
    190  }
    191 
    192  RefPtr<FakeSynthCallback> cb =
    193      new FakeSynthCallback((flags & eSuppressEvents) ? nullptr : aTask);
    194 
    195  aTask->Setup(cb);
    196 
    197  nsCOMPtr<nsIRunnable> runnable = new DispatchStart(aTask);
    198  NS_DispatchToMainThread(runnable);
    199 
    200  if (flags & eFail) {
    201    runnable = new DispatchError(aTask, aText);
    202    NS_DispatchToMainThread(runnable);
    203  } else if ((flags & eSuppressEnd) == 0) {
    204    runnable = new DispatchEnd(aTask, aText);
    205    NS_DispatchToMainThread(runnable);
    206  }
    207 
    208  return NS_OK;
    209 }
    210 
    211 // nsFakeSynthService
    212 
    213 NS_INTERFACE_MAP_BEGIN(nsFakeSynthServices)
    214  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    215  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
    216 NS_INTERFACE_MAP_END
    217 
    218 NS_IMPL_ADDREF(nsFakeSynthServices)
    219 NS_IMPL_RELEASE(nsFakeSynthServices)
    220 
    221 static void AddVoices(nsISpeechService* aService, const VoiceDetails* aVoices,
    222                      uint32_t aLength) {
    223  RefPtr<nsSynthVoiceRegistry> registry = nsSynthVoiceRegistry::GetInstance();
    224  for (uint32_t i = 0; i < aLength; i++) {
    225    NS_ConvertUTF8toUTF16 name(aVoices[i].name);
    226    NS_ConvertUTF8toUTF16 uri(aVoices[i].uri);
    227    NS_ConvertUTF8toUTF16 lang(aVoices[i].lang);
    228    // These services can handle more than one utterance at a time and have
    229    // several speaking simultaniously. So, aQueuesUtterances == false
    230    registry->AddVoice(aService, uri, name, lang, true, false);
    231    if (aVoices[i].defaultVoice) {
    232      registry->SetDefaultVoice(uri, true);
    233    }
    234  }
    235 
    236  registry->NotifyVoicesChanged();
    237 }
    238 
    239 void nsFakeSynthServices::Init() {
    240  mSynthService = new FakeSpeechSynth();
    241  AddVoices(mSynthService, sVoices, std::size(sVoices));
    242 }
    243 
    244 // nsIObserver
    245 
    246 NS_IMETHODIMP
    247 nsFakeSynthServices::Observe(nsISupports* aSubject, const char* aTopic,
    248                             const char16_t* aData) {
    249  MOZ_ASSERT(NS_IsMainThread());
    250  if (NS_WARN_IF(!(!strcmp(aTopic, "speech-synth-started")))) {
    251    return NS_ERROR_UNEXPECTED;
    252  }
    253 
    254  if (Preferences::GetBool("media.webspeech.synth.test")) {
    255    NS_DispatchToMainThread(NewRunnableMethod(
    256        "dom::nsFakeSynthServices::Init", this, &nsFakeSynthServices::Init));
    257  }
    258 
    259  return NS_OK;
    260 }
    261 
    262 // static methods
    263 
    264 nsFakeSynthServices* nsFakeSynthServices::GetInstance() {
    265  MOZ_ASSERT(NS_IsMainThread());
    266  if (!XRE_IsParentProcess()) {
    267    MOZ_ASSERT(false,
    268               "nsFakeSynthServices can only be started on main gecko process");
    269    return nullptr;
    270  }
    271 
    272  if (!sSingleton) {
    273    sSingleton = new nsFakeSynthServices();
    274    ClearOnShutdown(&sSingleton);
    275  }
    276 
    277  return sSingleton;
    278 }
    279 
    280 already_AddRefed<nsFakeSynthServices>
    281 nsFakeSynthServices::GetInstanceForService() {
    282  RefPtr<nsFakeSynthServices> picoService = GetInstance();
    283  return picoService.forget();
    284 }
    285 
    286 }  // namespace mozilla::dom