Unreviewed, rolling out r244627.
[WebKit-https.git] / Source / WebCore / platform / ios / PlatformSpeechSynthesizerIOS.mm
1 /*
2  * Copyright (C) 2013 Apple Inc.  All rights reserved.
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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #import "config.h"
26 #import "PlatformSpeechSynthesizer.h"
27
28 #if ENABLE(SPEECH_SYNTHESIS) && PLATFORM(IOS_FAMILY)
29
30 #import "PlatformSpeechSynthesisUtterance.h"
31 #import "PlatformSpeechSynthesisVoice.h"
32 #import <AVFoundation/AVSpeechSynthesis.h>
33 #import <wtf/BlockObjCExceptions.h>
34 #import <wtf/RetainPtr.h>
35 #import <wtf/SoftLinking.h>
36
37 SOFT_LINK_FRAMEWORK(AVFoundation)
38 SOFT_LINK_CLASS(AVFoundation, AVSpeechSynthesizer)
39 SOFT_LINK_CLASS(AVFoundation, AVSpeechUtterance)
40 SOFT_LINK_CLASS(AVFoundation, AVSpeechSynthesisVoice)
41
42 SOFT_LINK_CONSTANT(AVFoundation, AVSpeechUtteranceDefaultSpeechRate, float)
43 SOFT_LINK_CONSTANT(AVFoundation, AVSpeechUtteranceMaximumSpeechRate, float)
44
45 #define AVSpeechUtteranceDefaultSpeechRate getAVSpeechUtteranceDefaultSpeechRate()
46 #define AVSpeechUtteranceMaximumSpeechRate getAVSpeechUtteranceMaximumSpeechRate()
47
48 #define AVSpeechUtteranceClass getAVSpeechUtteranceClass()
49 #define AVSpeechSynthesisVoiceClass getAVSpeechSynthesisVoiceClass()
50
51 @interface WebSpeechSynthesisWrapper : NSObject<AVSpeechSynthesizerDelegate>
52 {
53     WebCore::PlatformSpeechSynthesizer* m_synthesizerObject;
54     // Hold a Ref to the utterance so that it won't disappear until the synth is done with it.
55     RefPtr<WebCore::PlatformSpeechSynthesisUtterance> m_utterance;
56
57     RetainPtr<AVSpeechSynthesizer> m_synthesizer;
58 }
59
60 - (WebSpeechSynthesisWrapper *)initWithSpeechSynthesizer:(WebCore::PlatformSpeechSynthesizer*)synthesizer;
61 - (void)speakUtterance:(RefPtr<WebCore::PlatformSpeechSynthesisUtterance>&&)utterance;
62
63 @end
64
65 @implementation WebSpeechSynthesisWrapper
66
67 - (WebSpeechSynthesisWrapper *)initWithSpeechSynthesizer:(WebCore::PlatformSpeechSynthesizer*)synthesizer
68 {
69     if (!(self = [super init]))
70         return nil;
71
72     m_synthesizerObject = synthesizer;
73     return self;
74 }
75
76 - (float)mapSpeechRateToPlatformRate:(float)rate
77 {
78     // WebSpeech says to go from .1 -> 10 (default 1)
79     // AVSpeechSynthesizer asks for 0 -> 1 (default. 5)
80     if (rate < 1)
81         rate *= AVSpeechUtteranceDefaultSpeechRate;
82     else
83         rate = AVSpeechUtteranceDefaultSpeechRate + ((rate - 1) * (AVSpeechUtteranceMaximumSpeechRate - AVSpeechUtteranceDefaultSpeechRate));
84
85     return rate;
86 }
87
88 - (void)speakUtterance:(RefPtr<WebCore::PlatformSpeechSynthesisUtterance>&&)utterance
89 {
90     // When speak is called we should not have an existing speech utterance outstanding.
91     ASSERT(!m_utterance);
92     ASSERT(utterance);
93
94     if (!utterance)
95         return;
96
97     BEGIN_BLOCK_OBJC_EXCEPTIONS
98     if (!m_synthesizer) {
99         m_synthesizer = adoptNS([allocAVSpeechSynthesizerInstance() init]);
100         [m_synthesizer setDelegate:self];
101     }
102
103     // Choose the best voice, by first looking at the utterance voice, then the utterance language,
104     // then choose the default language.
105     WebCore::PlatformSpeechSynthesisVoice* utteranceVoice = utterance->voice();
106     NSString *voiceLanguage = nil;
107     if (!utteranceVoice) {
108         if (utterance->lang().isEmpty())
109             voiceLanguage = [AVSpeechSynthesisVoiceClass currentLanguageCode];
110         else
111             voiceLanguage = utterance->lang();
112     } else
113         voiceLanguage = utterance->voice()->lang();
114
115     AVSpeechSynthesisVoice *avVoice = nil;
116     if (voiceLanguage)
117         avVoice = [AVSpeechSynthesisVoiceClass voiceWithLanguage:voiceLanguage];
118
119     AVSpeechUtterance *avUtterance = [AVSpeechUtteranceClass speechUtteranceWithString:utterance->text()];
120
121     [avUtterance setRate:[self mapSpeechRateToPlatformRate:utterance->rate()]];
122     [avUtterance setVolume:utterance->volume()];
123     [avUtterance setPitchMultiplier:utterance->pitch()];
124     [avUtterance setVoice:avVoice];
125     m_utterance = WTFMove(utterance);
126
127     [m_synthesizer speakUtterance:avUtterance];
128     END_BLOCK_OBJC_EXCEPTIONS
129 }
130
131 - (void)pause
132 {
133     if (!m_utterance)
134         return;
135
136     BEGIN_BLOCK_OBJC_EXCEPTIONS
137     [m_synthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];
138     END_BLOCK_OBJC_EXCEPTIONS
139 }
140
141 - (void)resume
142 {
143     if (!m_utterance)
144         return;
145
146     BEGIN_BLOCK_OBJC_EXCEPTIONS
147     [m_synthesizer continueSpeaking];
148     END_BLOCK_OBJC_EXCEPTIONS
149 }
150
151 - (void)cancel
152 {
153     if (!m_utterance)
154         return;
155
156     BEGIN_BLOCK_OBJC_EXCEPTIONS
157     [m_synthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
158     END_BLOCK_OBJC_EXCEPTIONS
159 }
160
161 - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didStartSpeechUtterance:(AVSpeechUtterance *)utterance
162 {
163     UNUSED_PARAM(synthesizer);
164     UNUSED_PARAM(utterance);
165     if (!m_utterance)
166         return;
167
168     m_synthesizerObject->client()->didStartSpeaking(*m_utterance);
169 }
170
171 - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
172 {
173     UNUSED_PARAM(synthesizer);
174     UNUSED_PARAM(utterance);
175     if (!m_utterance)
176         return;
177
178     // Clear the m_utterance variable in case finish speaking kicks off a new speaking job immediately.
179     RefPtr<WebCore::PlatformSpeechSynthesisUtterance> platformUtterance = m_utterance;
180     m_utterance = nullptr;
181
182     m_synthesizerObject->client()->didFinishSpeaking(*platformUtterance);
183 }
184
185 - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didPauseSpeechUtterance:(AVSpeechUtterance *)utterance
186 {
187     UNUSED_PARAM(synthesizer);
188     UNUSED_PARAM(utterance);
189     if (!m_utterance)
190         return;
191
192     m_synthesizerObject->client()->didPauseSpeaking(*m_utterance);
193 }
194
195 - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didContinueSpeechUtterance:(AVSpeechUtterance *)utterance
196 {
197     UNUSED_PARAM(synthesizer);
198     UNUSED_PARAM(utterance);
199     if (!m_utterance)
200         return;
201
202     m_synthesizerObject->client()->didResumeSpeaking(*m_utterance);
203 }
204
205 - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didCancelSpeechUtterance:(AVSpeechUtterance *)utterance
206 {
207     UNUSED_PARAM(synthesizer);
208     UNUSED_PARAM(utterance);
209     if (!m_utterance)
210         return;
211
212     // Clear the m_utterance variable in case finish speaking kicks off a new speaking job immediately.
213     RefPtr<WebCore::PlatformSpeechSynthesisUtterance> platformUtterance = m_utterance;
214     m_utterance = nullptr;
215
216     m_synthesizerObject->client()->didFinishSpeaking(*platformUtterance);
217 }
218
219 - (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer willSpeakRangeOfSpeechString:(NSRange)characterRange utterance:(AVSpeechUtterance *)utterance
220 {
221     UNUSED_PARAM(synthesizer);
222     UNUSED_PARAM(utterance);
223
224     if (!m_utterance)
225         return;
226
227     // iOS only supports word boundaries.
228     m_synthesizerObject->client()->boundaryEventOccurred(*m_utterance, WebCore::SpeechBoundary::SpeechWordBoundary, characterRange.location);
229 }
230
231 @end
232
233 namespace WebCore {
234
235 PlatformSpeechSynthesizer::PlatformSpeechSynthesizer(PlatformSpeechSynthesizerClient* client)
236     : m_speechSynthesizerClient(client)
237 {
238 }
239
240 PlatformSpeechSynthesizer::~PlatformSpeechSynthesizer()
241 {
242 }
243
244 void PlatformSpeechSynthesizer::initializeVoiceList()
245 {
246     BEGIN_BLOCK_OBJC_EXCEPTIONS
247     for (AVSpeechSynthesisVoice *voice in [AVSpeechSynthesisVoiceClass speechVoices]) {
248         NSString *language = [voice language];
249         bool isDefault = true;
250         NSString *voiceURI = [voice identifier];
251         NSString *name = [voice name];
252         m_voiceList.append(PlatformSpeechSynthesisVoice::create(voiceURI, name, language, true, isDefault));
253     }
254     END_BLOCK_OBJC_EXCEPTIONS
255 }
256
257 void PlatformSpeechSynthesizer::pause()
258 {
259     [m_platformSpeechWrapper.get() pause];
260 }
261
262 void PlatformSpeechSynthesizer::resume()
263 {
264     [m_platformSpeechWrapper.get() resume];
265 }
266
267 void PlatformSpeechSynthesizer::speak(RefPtr<PlatformSpeechSynthesisUtterance>&& utterance)
268 {
269     if (!m_platformSpeechWrapper)
270         m_platformSpeechWrapper = adoptNS([[WebSpeechSynthesisWrapper alloc] initWithSpeechSynthesizer:this]);
271
272     [m_platformSpeechWrapper.get() speakUtterance:utterance.get()];
273 }
274
275 void PlatformSpeechSynthesizer::cancel()
276 {
277     [m_platformSpeechWrapper.get() cancel];
278 }
279
280 } // namespace WebCore
281
282 #endif // ENABLE(SPEECH_SYNTHESIS) && PLATFORM(IOS_FAMILY)