capacitor-community / text-to-speech

⚡️ Capacitor plugin for synthesizing speech from text.
MIT License
96 stars 27 forks source link

getSupportedVoices() #112

Closed mar10droid3v closed 5 months ago

mar10droid3v commented 6 months ago

Plugin version: @capacitor-community/text-to-speech": "^3.0.1

Platform(s): android 12

Current behavior: Hi when testing on a real device TextToSpeech.getSupportedVoices() is returning error, "Comparison method violates its general contract!"

Any ideas?

migmax48 commented 5 months ago

same problem on speak method.

mar10droid3v commented 5 months ago

I have a working fix but am not a contributor and i dont know how to do this or publish/suggest a fix etc... or step on anyones toes! Its because you cant or shouldn't compare by using a hashcode. For now i just edit the TextToSpeech.java file locally by changing the getSupportedVoices method/function see below... if someone can get it to work and update the plugin would be great... my 2 cents and thanks! :)

public ArrayList<Voice> getSupportedVoicesOrdered() {
    Set<Voice> supportedVoices = tts.getVoices();
    ArrayList<Voice> orderedVoices = new ArrayList<Voice>();
    for (Voice supportedVoice : supportedVoices) {
        orderedVoices.add(supportedVoice);
    }

   /* Collections.sort(
        orderedVoices,
        new Comparator<Voice>() {
            public int compare(Voice v1, Voice v2) {
                return v1.hashCode() - v2.hashCode();
            }
        }
    ); */
    //sort by name instead...
    Collections.sort(
        orderedVoices,
        new Comparator<Voice>() {
          public int compare(Voice v1, Voice v2) {
            return v1.getName().compareTo(v2.getName());
          }
        }
      );

    return orderedVoices;
}

public JSArray getSupportedVoices() {
    ArrayList<JSObject> voices = new ArrayList<>();
    ArrayList<Voice> supportedVoices = getSupportedVoicesOrdered();
    for (Voice supportedVoice : supportedVoices) {
        JSObject obj = this.convertVoiceToJSObject(supportedVoice);
        voices.add(obj);
    }
    JSArray result = JSArray.from(voices.toArray());
    return result;
}
robingenz commented 5 months ago

PRs are welcome!

ecc521 commented 5 months ago

@mar10droid3v Pretty sure I'm the one that wrote this one. Apologies for the bug here. Should be pretty easy to send in a PR. If you don't send in a PR, I'll probably write one in around a month when I get around to it.

A little surprised to see an issue here - hashCode should be constant for any given object, so transitivity should be preserved. Have you logged the hashCodes and taken a look at what is going on there?

A consistent sorting mechanism is needed, but the mechanism itself is irrelevant, so name should be totally fine.

As for now, if you don't use the voice selection everything will work just fine.

ecc521 commented 5 months ago

@mar10droid3v @migmax48 What devices are you using? I'm unable to reproduce this issue.

mar10droid3v commented 5 months ago

@ecc521 hey tucker. happens on xiaomi devices. using name fixed it.

migmax48 commented 5 months ago

@ecc521 Hi Tucker. I confirm that the problem occurred on Xiaomi devices. By moving the code before the angular class definition the problem was solved.

hpflatorre commented 5 months ago

@ecc521 Same issue here on a Samsung SM-X210 tablet. The plugin works after the tablet is factory reset. The issue starts after changing the default TTS from Samsung Voice to Google Voice on System settings. Changing it back to Samsung TTS stops the error.

Sending plugin error: {"save":false,"callbackId":"85276539","pluginId":"TextToSpeech","methodName":"getSupportedVoices","success":false,"error":{"message":"Comparison method violates its general contract!"}}

I've also tested @mar10droid3v solution and it works well.