Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(216)

Side by Side Diff: chrome/browser/speech/tts_mac.mm

Issue 12636005: Fix crash in NSSpeechSynthesizer by explicitly retaining NSString. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Fix link to openradar Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <string> 5 #include <string>
6 6
7 #include "base/memory/scoped_nsobject.h" 7 #include "base/memory/scoped_nsobject.h"
8 #include "base/memory/singleton.h" 8 #include "base/memory/singleton.h"
9 #include "base/sys_string_conversions.h" 9 #include "base/sys_string_conversions.h"
10 #include "base/values.h" 10 #include "base/values.h"
11 #include "chrome/browser/extensions/extension_function.h" 11 #include "chrome/browser/extensions/extension_function.h"
12 #include "chrome/browser/speech/tts_controller.h" 12 #include "chrome/browser/speech/tts_controller.h"
13 #include "chrome/browser/speech/tts_platform.h" 13 #include "chrome/browser/speech/tts_platform.h"
14 14
15 #import <Cocoa/Cocoa.h> 15 #import <Cocoa/Cocoa.h>
16 16
17 class TtsPlatformImplMac; 17 class TtsPlatformImplMac;
18 18
19 @interface ChromeTtsDelegate : NSObject <NSSpeechSynthesizerDelegate> { 19 @interface ChromeTtsDelegate : NSObject <NSSpeechSynthesizerDelegate> {
20 @private 20 @private
21 TtsPlatformImplMac* ttsImplMac_; // weak. 21 TtsPlatformImplMac* ttsImplMac_; // weak.
22 } 22 }
23 23
24 - (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac; 24 - (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac;
25 25
26 @end 26 @end
27 27
28 // Subclass of NSSpeechSynthesizer that takes an utterance
29 // string on initialization, retains it and only allows it
30 // to be spoken once.
31 //
32 // We construct a new NSSpeechSynthesizer for each utterance, for
33 // two reasons:
34 // 1. To associate delegate callbacks with a particular utterance,
35 // without assuming anything undocumented about the protocol.
36 // 2. To work around http://openradar.appspot.com/radar?id=2854403,
37 // where Nuance voices don't retain the utterance string and
38 // crash when trying to call willSpeakWord.
39 @interface SingleUseSpeechSynthesizer : NSSpeechSynthesizer {
40 @private
41 scoped_nsobject<NSString> utterance_;
42 bool didSpeak_;
43 }
44
45 - (id)initWithUtterance:(NSString*)utterance;
46 - (bool)startSpeakingRetainedUtterance;
47 - (bool)startSpeakingString:(NSString*)utterance;
48
49 @end
50
28 class TtsPlatformImplMac : public TtsPlatformImpl { 51 class TtsPlatformImplMac : public TtsPlatformImpl {
29 public: 52 public:
30 virtual bool PlatformImplAvailable() OVERRIDE { 53 virtual bool PlatformImplAvailable() OVERRIDE {
31 return true; 54 return true;
32 } 55 }
33 56
34 virtual bool Speak( 57 virtual bool Speak(
35 int utterance_id, 58 int utterance_id,
36 const std::string& utterance, 59 const std::string& utterance,
37 const std::string& lang, 60 const std::string& lang,
(...skipping 12 matching lines...) Expand all
50 int char_index, 73 int char_index,
51 const std::string& error_message); 74 const std::string& error_message);
52 75
53 // Get the single instance of this class. 76 // Get the single instance of this class.
54 static TtsPlatformImplMac* GetInstance(); 77 static TtsPlatformImplMac* GetInstance();
55 78
56 private: 79 private:
57 TtsPlatformImplMac(); 80 TtsPlatformImplMac();
58 virtual ~TtsPlatformImplMac(); 81 virtual ~TtsPlatformImplMac();
59 82
60 scoped_nsobject<NSSpeechSynthesizer> speech_synthesizer_; 83 scoped_nsobject<SingleUseSpeechSynthesizer> speech_synthesizer_;
61 scoped_nsobject<ChromeTtsDelegate> delegate_; 84 scoped_nsobject<ChromeTtsDelegate> delegate_;
62 int utterance_id_; 85 int utterance_id_;
63 std::string utterance_; 86 std::string utterance_;
64 bool sent_start_event_; 87 bool sent_start_event_;
65 88
66 friend struct DefaultSingletonTraits<TtsPlatformImplMac>; 89 friend struct DefaultSingletonTraits<TtsPlatformImplMac>;
67 90
68 DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplMac); 91 DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplMac);
69 }; 92 };
70 93
71 // static 94 // static
72 TtsPlatformImpl* TtsPlatformImpl::GetInstance() { 95 TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
73 return TtsPlatformImplMac::GetInstance(); 96 return TtsPlatformImplMac::GetInstance();
74 } 97 }
75 98
76 bool TtsPlatformImplMac::Speak( 99 bool TtsPlatformImplMac::Speak(
77 int utterance_id, 100 int utterance_id,
78 const std::string& utterance, 101 const std::string& utterance,
79 const std::string& lang, 102 const std::string& lang,
80 const UtteranceContinuousParameters& params) { 103 const UtteranceContinuousParameters& params) {
104 // TODO: convert SSML to SAPI xml. http://crbug.com/88072
105 utterance_ = utterance;
106
107 NSString* utterance_nsstring =
108 [NSString stringWithUTF8String:utterance_.c_str()];
109
81 // Deliberately construct a new speech synthesizer every time Speak is 110 // Deliberately construct a new speech synthesizer every time Speak is
82 // called, otherwise there's no way to know whether calls to the delegate 111 // called, otherwise there's no way to know whether calls to the delegate
83 // apply to the current utterance or a previous utterance. In 112 // apply to the current utterance or a previous utterance. In
84 // experimentation, the overhead of constructing and destructing a 113 // experimentation, the overhead of constructing and destructing a
85 // NSSpeechSynthesizer is minimal. 114 // NSSpeechSynthesizer is minimal.
86 speech_synthesizer_.reset([[NSSpeechSynthesizer alloc] init]); 115 speech_synthesizer_.reset(
116 [[SingleUseSpeechSynthesizer alloc]
117 initWithUtterance:utterance_nsstring]);
87 [speech_synthesizer_ setDelegate:delegate_]; 118 [speech_synthesizer_ setDelegate:delegate_];
88 119
89 utterance_id_ = utterance_id; 120 utterance_id_ = utterance_id;
90 sent_start_event_ = false; 121 sent_start_event_ = false;
91 122
92 // TODO: convert SSML to SAPI xml. http://crbug.com/88072
93 utterance_ = utterance;
94
95 // TODO: support languages other than the default: crbug.com/88059 123 // TODO: support languages other than the default: crbug.com/88059
96 124
97 if (params.rate >= 0.0) { 125 if (params.rate >= 0.0) {
98 // The TTS api defines rate via words per minute. Let 200 be the default. 126 // The TTS api defines rate via words per minute. Let 200 be the default.
99 [speech_synthesizer_ 127 [speech_synthesizer_
100 setObject:[NSNumber numberWithInt:params.rate * 200] 128 setObject:[NSNumber numberWithInt:params.rate * 200]
101 forProperty:NSSpeechRateProperty error:nil]; 129 forProperty:NSSpeechRateProperty error:nil];
102 } 130 }
103 131
104 if (params.pitch >= 0.0) { 132 if (params.pitch >= 0.0) {
105 // The TTS api allows an approximate range of 30 to 65 for speech pitch. 133 // The TTS api allows an approximate range of 30 to 65 for speech pitch.
106 [speech_synthesizer_ 134 [speech_synthesizer_
107 setObject: [NSNumber numberWithInt:(params.pitch * 17 + 30)] 135 setObject: [NSNumber numberWithInt:(params.pitch * 17 + 30)]
108 forProperty:NSSpeechPitchBaseProperty error:nil]; 136 forProperty:NSSpeechPitchBaseProperty error:nil];
109 } 137 }
110 138
111 if (params.volume >= 0.0) { 139 if (params.volume >= 0.0) {
112 [speech_synthesizer_ 140 [speech_synthesizer_
113 setObject: [NSNumber numberWithFloat:params.volume] 141 setObject: [NSNumber numberWithFloat:params.volume]
114 forProperty:NSSpeechVolumeProperty error:nil]; 142 forProperty:NSSpeechVolumeProperty error:nil];
115 } 143 }
116 144
117 return [speech_synthesizer_ startSpeakingString: 145 return [speech_synthesizer_ startSpeakingRetainedUtterance];
118 [NSString stringWithUTF8String: utterance.c_str()]];
119 } 146 }
120 147
121 bool TtsPlatformImplMac::StopSpeaking() { 148 bool TtsPlatformImplMac::StopSpeaking() {
122 if (speech_synthesizer_.get()) { 149 if (speech_synthesizer_.get()) {
123 [speech_synthesizer_ stopSpeaking]; 150 [speech_synthesizer_ stopSpeaking];
124 speech_synthesizer_.reset(nil); 151 speech_synthesizer_.reset(nil);
125 } 152 }
126 return true; 153 return true;
127 } 154 }
128 155
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 - (void)speechSynthesizer:(NSSpeechSynthesizer*)sender 226 - (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
200 didEncounterErrorAtIndex:(NSUInteger)character_index 227 didEncounterErrorAtIndex:(NSUInteger)character_index
201 ofString:(NSString*)string 228 ofString:(NSString*)string
202 message:(NSString*)message { 229 message:(NSString*)message {
203 std::string message_utf8 = base::SysNSStringToUTF8(message); 230 std::string message_utf8 = base::SysNSStringToUTF8(message);
204 ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_ERROR, character_index, 231 ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_ERROR, character_index,
205 message_utf8); 232 message_utf8);
206 } 233 }
207 234
208 @end 235 @end
236
237 @implementation SingleUseSpeechSynthesizer
238
239 - (id)initWithUtterance:(NSString*)utterance {
240 self = [super init];
241 if (self) {
242 utterance_.reset([utterance retain]);
243 didSpeak_ = false;
244 }
245 return self;
246 }
247
248 - (bool)startSpeakingRetainedUtterance {
249 CHECK(!didSpeak_);
250 CHECK(utterance_);
251 didSpeak_ = true;
252 return [super startSpeakingString:utterance_];
253 }
254
255 - (bool)startSpeakingString:(NSString*)utterance {
256 CHECK(false);
257 return false;
258 }
259
260 @end
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698