[EFL] Utilize espeak as a synthesizer back-end for WebSpeech
[WebKit-https.git] / Source / WebCore / platform / efl / PlatformSpeechSynthesisProviderEfl.cpp
1 /*
2  * Copyright (C) 2014 Samsung Electronics
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "PlatformSpeechSynthesisProviderEfl.h"
28
29 #if ENABLE(SPEECH_SYNTHESIS)
30
31 #include <NotImplemented.h>
32 #include <PlatformSpeechSynthesisUtterance.h>
33 #include <PlatformSpeechSynthesisVoice.h>
34 #include <PlatformSpeechSynthesizer.h>
35 #include <wtf/text/CString.h>
36
37 namespace WebCore {
38
39 PlatformSpeechSynthesisProviderEfl::PlatformSpeechSynthesisProviderEfl(PlatformSpeechSynthesizer* client)
40     : m_isEngineStarted(false)
41     , m_platformSpeechSynthesizer(client)
42 {
43 }
44
45 PlatformSpeechSynthesisProviderEfl::~PlatformSpeechSynthesisProviderEfl()
46 {
47 }
48
49 int PlatformSpeechSynthesisProviderEfl::convertRateToEspeakValue(float rate) const
50 {
51     // The normal value that Espeak expects is 175, minimum is 80 and maximum 450
52     return espeakRATE_NORMAL * rate;
53 }
54
55 int PlatformSpeechSynthesisProviderEfl::convertVolumeToEspeakValue(float volume) const
56 {
57     // 0 = silence, 100 = normal, greater values may produce distortion
58     return volume * 100;
59 }
60
61 int PlatformSpeechSynthesisProviderEfl::convertPitchToEspeakValue(float pitch) const
62 {
63     // 0 = minimum, 50 = normal, 100 = maximum
64     return pitch * 50;
65 }
66
67 String PlatformSpeechSynthesisProviderEfl::voiceName(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance) const
68 {
69     if (!m_platformSpeechSynthesizer)
70         return String();
71
72     if (!utterance->lang().isEmpty()) {
73         const String& language = utterance->lang();
74         const Vector<RefPtr<PlatformSpeechSynthesisVoice>>& voiceList = m_platformSpeechSynthesizer->voiceList();
75         for (const auto& voice : voiceList) {
76             // Espeak adds an empty character at the beginning of the language
77             unsigned length = voice->lang().length();
78             String lang = voice->lang().substring(1, length);
79             if (equalIgnoringCase(language, lang))
80                 return voice->name();
81         }
82     }
83
84     espeak_VOICE* espeakVoice = currentVoice();
85     ASSERT(espeakVoice);
86     return ASCIILiteral(espeakVoice->name);
87 }
88
89 bool PlatformSpeechSynthesisProviderEfl::engineInit()
90 {
91     if (!m_isEngineStarted) {
92         if (!(m_isEngineStarted = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, 0, 0) != EE_INTERNAL_ERROR))
93             return false;
94         espeak_SetVoiceByName("default");
95     }
96     return true;
97 }
98
99 espeak_VOICE* PlatformSpeechSynthesisProviderEfl::currentVoice() const
100 {
101     return espeak_GetCurrentVoice();
102 }
103
104 void PlatformSpeechSynthesisProviderEfl::initializeVoiceList(Vector<RefPtr<PlatformSpeechSynthesisVoice>>& voiceList)
105 {
106     if (!engineInit()) {
107         fireSpeechEvent(SpeechError);
108         return;
109     }
110
111     espeak_VOICE* espeakVoice = currentVoice();
112     ASSERT(espeakVoice);
113     String currentLanguage = ASCIILiteral(espeakVoice->languages);
114
115     const espeak_VOICE** voices = espeak_ListVoices(nullptr);
116     if (!voices) {
117         fireSpeechEvent(SpeechError);
118         return;
119     }
120
121     // Voices array is terminated by the nullptr
122     for (int i = 0; voices[i]; i++) {
123         const espeak_VOICE* voice = voices[i];
124         String id = ASCIILiteral(voice->identifier);
125         String name = ASCIILiteral(voice->name);
126         String language = ASCIILiteral(voice->languages);
127         voiceList.append(PlatformSpeechSynthesisVoice::create(id, name, language, true, language == currentLanguage));
128     }
129 }
130
131 void PlatformSpeechSynthesisProviderEfl::pause()
132 {
133     notImplemented();
134 }
135
136 void PlatformSpeechSynthesisProviderEfl::resume()
137 {
138     notImplemented();
139 }
140
141 void PlatformSpeechSynthesisProviderEfl::speak(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance)
142 {
143     if (!engineInit() || !utterance) {
144         fireSpeechEvent(SpeechError);
145         return;
146     }
147
148     m_utterance = utterance;
149     String voice = voiceName(m_utterance);
150     espeak_SetVoiceByName(voice.utf8().data());
151     espeak_SetParameter(espeakRATE, convertRateToEspeakValue(m_utterance->rate()), 0);
152     espeak_SetParameter(espeakVOLUME, convertVolumeToEspeakValue(m_utterance->volume()), 0);
153     espeak_SetParameter(espeakPITCH, convertPitchToEspeakValue(m_utterance->pitch()), 0);
154
155     String textToRead = m_utterance->text();
156     espeak_ERROR err = espeak_Synth(textToRead.utf8().data(), textToRead.length(), 0, POS_CHARACTER, 0, espeakCHARS_AUTO, 0, nullptr);
157     if (err == EE_INTERNAL_ERROR) {
158         fireSpeechEvent(SpeechError);
159         m_utterance = nullptr;
160         return;
161     }
162
163     fireSpeechEvent(SpeechStart);
164 }
165
166 void PlatformSpeechSynthesisProviderEfl::cancel()
167 {
168     if (!m_isEngineStarted || !m_utterance)
169         return;
170
171     if (espeak_Cancel() == EE_INTERNAL_ERROR) {
172         fireSpeechEvent(SpeechError);
173         m_utterance = nullptr;
174         return;
175     }
176     fireSpeechEvent(SpeechCancel);
177     m_utterance = nullptr;
178 }
179
180 void PlatformSpeechSynthesisProviderEfl::fireSpeechEvent(SpeechEvent speechEvent)
181 {
182     switch (speechEvent) {
183     case SpeechStart:
184         m_platformSpeechSynthesizer->client()->didStartSpeaking(m_utterance);
185         break;
186     case SpeechPause:
187         m_platformSpeechSynthesizer->client()->didPauseSpeaking(m_utterance);
188         break;
189     case SpeechResume:
190         m_platformSpeechSynthesizer->client()->didResumeSpeaking(m_utterance);
191         break;
192     case SpeechError:
193         m_isEngineStarted = false;
194     case SpeechCancel:
195         m_platformSpeechSynthesizer->client()->speakingErrorOccurred(m_utterance);
196         break;
197     default:
198         ASSERT_NOT_REACHED();
199     };
200 }
201
202 } // namespace WebCore
203
204 #endif