Sign in Join
Wiki copytemporary

macOS Text-To-Speech: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: macOS Text-To-Speech

OldNewLine
1+*(macOS only)*
2+
3+## Overview
4+
5+This article covers some ways of generating speech from text strings using the native macOS text-to-speech facilities. This is known as text-to-speech (TTS) or speech synthesis. This synthesized speech is an essential aid to those users with attention or vision disabilities. It is also useful when you want to draw the user’s attention to something important when they might be distracted by something else on the screen. For example, you may want your application to incorporate the capability to speak its dialog box messages to the user.
6+
7+There are two distinct methods available to macSO developers: using code to generate speech from text and using utility tools provided with the operating system.
8+
9+### Using code
10+
11+There are three options. In their order of evolution, they are:
12+
13+- The Speech Synthesis Manager, formerly called the Speech Manager, is a standardized method for macOS applications to generate synthesized speech. The Speech Synthesis Manager is available from macOS 10.0+ (Cheetah) although some of its functions have been deprecated and some new ones were introduced with macOS 10.5 (Leopard).
14+
15+- Using an NSSpeechSynthesizer object, you can make your application speak a word, phrase, or sentence to the user. NSSpeechSynthesizer is available from macOS 10.3+ (Panther). The Cocoa interface to speech synthesis in macOS.
16+
17+- Using the AVSpeechSynthesizer object that produces synthesized speech from text utterances and provides controls for monitoring or controlling the progress of ongoing speech. Available from macOS 10.14+ (Mojave).
18+
19+### Using utilities
20+
21+There are two utility tools provided with macOS that enable you to generate speech from text:
22+
23+- The say utility;
24+- The osacscript utility.
25+
26+## Generating speech from code
27+
28+### Using Speech Synthesis Manager
29+
30+The Speech Synthesis Manager, formerly called the Speech Manager, is the part of the macOS that provides a standardized method for applications to generate synthesized speech. While it is the easiest method to use, a number of its functions are now deprecated (see the Apple documentation below for details).
31+
32+A simple example using the *SpeakString* function:
33+
34+``` pascal
35+unit Unit1;
36+
37+{$mode objfpc}{$H+}
38+{$modeswitch objectivec2}
39+
40+interface
41+
42+uses
43+ Classes, SysUtils, Forms, Dialogs, StdCtrls, MacOSAll;
44+
45+type
46+
47+ { TForm1 }
48+
49+ TForm1 = Class(TForm)
50+ Button1: TButton;
51+ procedure Button1Click(Sender: TObject);
52+ private
53+
54+ public
55+
56+ end;
57+
58+var
59+ Form1: TForm1;
60+
61+implementation
62+
63+{$R *.lfm}
64+
65+function mySpeechMgrPresent: OSErr;
66+var
67+ myErr : OSErr;
68+ myFeature: LongInt = 0;
69+begin
70+{Feature being tested}
71+myerr := Gestalt(gestaltSpeechAttr, myFeature);
72+
73+{Test Speech Manager present bit}
74+if (myErr = noErr) and ((((myFeature) shr (gestaltSpeechMgrPresent)) and 1)=1) then
75+ begin
76+ myErr := SpeakString('The Speech Manager is working and is almost done.');
77+
78+ {Wait until synthesizer is done speaking}
79+ while (SpeechBusy <> 0) do
80+ begin
81+ Application.ProcessMessages;
82+ end;
83+
84+ mySpeechMgrPresent := myErr;
85+ end
86+else
87+ mySpeechMgrPresent := -1;
88+end;
89+
90+{ TForm1 }
91+
92+procedure TForm1.Button1Click(Sender: TObject);
93+begin
94+ if(mySpeechMgrPresent = noErr) then
95+ ShowMessage('Speech Manager present: No error')
96+ else
97+ ShowMessage('Speech Manager not present: Error');
98+
99+end;
100+
101+end.
102+```
103+
104+Unfortunately the *SpeakString* function was deprecated in macOS 10.8 (Mountain Lion) - it is also limited to Pascal strings (ie a string length of 255 characters). The replacement function *SpeakCFString* introduced in macOS 10.5 (Leopard) is a little more complicated, as demonstrated in the reworked example below, and is not limited to 255-character strings:
105+
106+``` pascal
107+unit Unit1;
108+
109+{$mode objfpc}{$H+}
110+{$modeswitch objectivec2}
111+
112+interface
113+
114+uses
115+ Classes, SysUtils, Forms, Dialogs, StdCtrls, MacOSAll;
116+
117+type
118+
119+ { TForm1 }
120+
121+ TForm1 = Class(TForm)
122+ Button1: TButton;
123+ procedure Button1Click(Sender: TObject);
124+ private
125+
126+ public
127+
128+ end;
129+
130+var
131+ Form1: TForm1;
132+
133+implementation
134+
135+{$R *.lfm}
136+
137+function MySpeechMgrPresent: OSErr;
138+var
139+ myErr : OSErr;
140+ myFeature: LongInt = 0;
141+ mySpChan : SpeechChannelRecordPtr = Nil;
142+begin
143+{Feature being tested}
144+myerr := Gestalt(gestaltSpeechAttr, myFeature);
145+
146+{Test Speech Manager present bit}
147+if (myErr = noErr) and ((((myFeature) shr (gestaltSpeechMgrPresent)) and 1)=1) then
148+ begin
149+ myErr := NewSpeechChannel(Nil, mySpChan);
150+ myErr := SpeakCFString(mySpChan, CFStr('The Speech Manager is working and is almost done.'), Nil);
151+
152+ {Wait until synthesizer is done speaking}
153+ while (SpeechBusy <> 0) do
154+ begin
155+ Application.ProcessMessages;
156+ end;
157+
158+ mySpeechMgrPresent := myErr;
159+ end
160+else
161+ mySpeechMgrPresent := -1;
162+end;
163+
164+{ TForm1 }
165+
166+procedure TForm1.Button1Click(Sender: TObject);
167+begin
168+ if(mySpeechMgrPresent = noErr) then
169+ ShowMessage('Speech Manager present: No error')
170+ else
171+ ShowMessage('Speech Manager not present: Error');
172+end;
173+
174+end.
175+```
176+
177+See the Apple documentation below for more details of the Speech Synthesis Manager's functionality.
178+
179+### Using NSSpeechSynthesizer
180+
181+Using an NSSpeechSynthesizer object to “pronounce” text is easy. You initialize the object with a voice and send a startSpeakingString: message to it, passing in an NSString object representing the text to speak.
182+
183+Source: [forum post by kogs](http://forum.lazarus.freepascal.org/index.php/topic,23890.msg143222.html#msg143222) and contributions by skalogryz (updated to compile with Lazarus 2.1 and FPC 3.3.1 by Trev):
184+
185+The code for the speech synthesis object, speechsynthesizer.pas:
186+
187+``` pascal
188+{ Parsed from Appkit.framework NSSpeechSynthesizerSpeechSynthesize.h }
189+unit speechsynthesizer;
190+
191+{$mode objfpc}{$H+}
192+{$modeswitch objectivec1}
193+
194+interface
195+
196+{$linkframework AppKit}
197+
198+uses
199+ Classes, MacOSAll, CocoaUtils; //, CarbonProc; Omit for Cocoa compilation
200+
201+type
202+
203+{ NSSpeechSynthesizer }
204+
205+ NSSpeechSynthesizer = objcclass external (NSObject)
206+ private
207+ _privateNSSpeechSynthesizerVars: id;
208+ // function and procedure
209+ // init with voice
210+ function initWithVoice(voice_: NSString): id; message 'initWithVoice:';
211+ // Speaking
212+ function startSpeakingString(string_: NSString): Boolean; message 'startSpeakingString:';
213+// function startSpeakingString_toURL(string_: NSString; url: NSURL): Boolean; message 'startSpeakingString:toURL:';
214+ function isSpeaking: Boolean; message 'isSpeaking';
215+ procedure stopSpeaking; message 'stopSpeaking';
216+// procedure stopSpeakingAtBoundary(boundary: NSSpeechBoundary); message 'stopSpeakingAtBoundary:';
217+// procedure pauseSpeakingAtBoundary(boundary: NSSpeechBoundary); message 'pauseSpeakingAtBoundary:';
218+ procedure continueSpeaking; message 'continueSpeaking';
219+ function delegate: NSObject; message 'delegate';
220+ procedure setDelegate(anObject: NSObject); message 'setDelegate:';
221+ // voice
222+ function voice: NSString; message 'voice';
223+ function setVoice(voice_: NSString): Boolean; message 'setVoice:';
224+ // rate
225+ function rate: single; message 'rate';
226+ procedure setRate(rate_: single); message 'setRate:';
227+ // volume
228+ function volume: single; message 'volume';
229+ procedure setVolume(volume_: single); message 'setVolume:';
230+// function usesFeedbackWindow: Boolean; message 'usesFeedbackWindow';
231+// procedure setUsesFeedbackWindow(flag: Boolean); message 'setUsesFeedbackWindow:';
232+// procedure addSpeechDictionary(speechDictionary: NSDictionary); message 'addSpeechDictionary:';
233+ // phonemes from text
234+ function phonemesFromText(text: NSString): NSString; message 'phonemesFromText:';
235+// function objectForProperty_error(property_: NSString; outError: NSErrorPointer): id; message 'objectForProperty:error:';
236+// function setObject_forProperty_error(object_: id; property_: NSString; outError: NSErrorPointer): Boolean; message 'setObject:forProperty:error:';
237+// class function isAnyApplicationSpeaking: Boolean; message 'isAnyApplicationSpeaking';
238+// class function defaultVoice: NSString; message 'defaultVoice';
239+// class function availableVoices: NSArray; message 'availableVoices';
240+// class function attributesForVoice(voice_: NSString): NSDictionary; message 'attributesForVoice:';
241+ end;
242+
243+ TSpeechDelegate = objcclass;
244+
245+ { TSpeechSynthesize }
246+
247+ TSpeechSynthesizer = class
248+ private
249+ fOnFinish : TNotifyEvent;
250+ // Speech are the Synthesizer and the Delegate
251+ SS: NSSpeechSynthesizer;
252+ Del: TSpeechDelegate;
253+ function NSStr(const stringA: String): NSString;
254+ // Voice
255+ function GetVoice: String;
256+ procedure SetVoice(stringVoice: String);
257+ // Rate
258+ function GetRate: Integer;
259+ procedure SetRate(integerRate: Integer);
260+ // Volume
261+ function GetVolume: Integer;
262+ procedure SetVolume(integerVolume: Integer);
263+ // AllocDelegate
264+ procedure AllocDelegate;
265+ protected
266+ procedure DoFinishedSpeaking;
267+ public
268+ constructor Create;
269+ constructor Create(stringVoice: String);
270+ destructor Destroy; override;
271+ // Speaking
272+ function StartSpeakingString(stringA: String): Boolean;
273+ procedure StopSpeaking;
274+ function IsSpeaking: Boolean; // speaking is yes/no
275+ procedure ContinueSpeaking;
276+ // Phonemes from text
277+ function PhonemesFromText(stringText: String): String;
278+ // Voice
279+ property Voice: String read GetVoice write SetVoice;
280+ // Rate
281+ property Rate: Integer read GetRate write SetRate;
282+ // Volume
283+ property Volume: Integer read GetVolume write SetVolume;
284+ // Notification on end of speech
285+ property OnFinish: TNotifyEvent read fOnFinish write fOnFinish;
286+ end;
287+
288+ { TSpeechDelegate }
289+
290+ TSpeechDelegate = objcclass(NSObject)
291+ public
292+ Obj : TSpeechSynthesizer;
293+ procedure SpeechSynthesizer_DidFinishSpeaking(sender: NSSpeechSynthesizer;
294+ finishedSpeakingSuccess: Boolean); message
295+ 'speechSynthesizer:didFinishSpeaking:';
296+ {procedure speechSynthesizer_willSpeakWord_ofString(sender: NSSpeechSynthesizer; characterRange: NSRange; string_: NSString); message 'speechSynthesizer:willSpeakWord:ofString:';
297+ procedure speechSynthesizer_willSpeakPhoneme(sender: NSSpeechSynthesizer; phonemeOpcode: cshort); message 'speechSynthesizer:willSpeakPhoneme:';
298+ procedure speechSynthesizer_didEncounterErrorAtIndex_ofString_message(sender: NSSpeechSynthesizer; characterIndex: NSUInteger; string_: NSString; message: NSString); message 'speechSynthesizer:didEncounterErrorAtIndex:ofString:message:';
299+ procedure speechSynthesizer_didEncounterSyncMessage(sender: NSSpeechSynthesizer; message: NSString); message 'speechSynthesizer:didEncounterSyncMessage:';}
300+ end;
301+
302+function NSStringToString(ns: NSString): String;
303+
304+implementation
305+
306+{-------------------------------------------------------------------------------
307+ function NSStringToString
308+-------------------------------------------------------------------------------}
309+function NSStringToString(ns: NSString): String;
310+begin
311+ Result := CFStringToStr(CFStringRef(ns));
312+end;
313+
314+//------------------------------------------------------------------------------
315+
316+{ TSpeechDelegate }
317+
318+{-------------------------------------------------------------------------------
319+ procedure SpeechSynthesizer_DidFinishSpeaking
320+-------------------------------------------------------------------------------}
321+procedure TSpeechDelegate.SpeechSynthesizer_DidFinishSpeaking(
322+ sender: NSSpeechSynthesizer; finishedSpeakingSuccess: Boolean);
323+begin
324+ if Assigned(Obj) then Obj.DoFinishedSpeaking;
325+end;
326+
327+//------------------------------------------------------------------------------
328+
329+{ TSpeechSynthesize }
330+
331+{-------------------------------------------------------------------------------
332+ function NSStr
333+-------------------------------------------------------------------------------}
334+function TSpeechSynthesizer.NSStr(const stringA: String): NSString;
335+ begin
336+ // converting string to NSString (CFStringRef and NSString are interchangable)
337+ Result := NSString(CFStr(PChar(stringA)));
338+ end;
339+
340+{-------------------------------------------------------------------------------
341+ function StartSpeakingString
342+-------------------------------------------------------------------------------}
343+function TSpeechSynthesizer.StartSpeakingString(stringA: String): Boolean;
344+begin
345+ Result := SS.startSpeakingString(NSStr(stringA));
346+end;
347+
348+{-------------------------------------------------------------------------------
349+ procedure StopSpeaking
350+-------------------------------------------------------------------------------}
351+procedure TSpeechSynthesizer.StopSpeaking;
352+begin
353+ SS.stopSpeaking;
354+end;
355+
356+{-------------------------------------------------------------------------------
357+ function IsSpeaking
358+-------------------------------------------------------------------------------}
359+function TSpeechSynthesizer.IsSpeaking: Boolean;
360+begin
361+ Result := False;
362+ Result := SS.isSpeaking;
363+end;
364+
365+{-------------------------------------------------------------------------------
366+ procedure ContinueSpeakinG
367+-------------------------------------------------------------------------------}
368+procedure TSpeechSynthesizer.ContinueSpeaking;
369+begin
370+ SS.continueSpeaking;
371+end;
372+
373+{-------------------------------------------------------------------------------
374+ function GetVoice
375+-------------------------------------------------------------------------------}
376+function TSpeechSynthesizer.GetVoice: String;
377+begin
378+ Result := NSStringToString(SS.voice);
379+end;
380+
381+{-------------------------------------------------------------------------------
382+ procedure SetVoice
383+-------------------------------------------------------------------------------}
384+procedure TSpeechSynthesizer.SetVoice(stringVoice: String);
385+begin
386+ SS.setVoice(NSStr(stringVoice));
387+end;
388+
389+{-------------------------------------------------------------------------------
390+ function GetRate
391+-------------------------------------------------------------------------------}
392+function TSpeechSynthesizer.GetRate: Integer;
393+begin
394+ Result := Integer(SS.rate);
395+end;
396+
397+{-------------------------------------------------------------------------------
398+ procedure SetRate
399+-------------------------------------------------------------------------------}
400+procedure TSpeechSynthesizer.SetRate(integerRate: Integer);
401+begin
402+ SS.setRate(single(integerRate));
403+end;
404+
405+{-------------------------------------------------------------------------------
406+ function GetVolume
407+-------------------------------------------------------------------------------}
408+function TSpeechSynthesizer.GetVolume: Integer;
409+begin
410+ Result := Integer(SS.rate);
411+end;
412+
413+{-------------------------------------------------------------------------------
414+ procedure SetRate
415+-------------------------------------------------------------------------------}
416+procedure TSpeechSynthesizer.SetVolume(integerVolume: Integer);
417+begin
418+ SS.setRate(Integer(integerVolume));
419+end;
420+
421+{-------------------------------------------------------------------------------
422+ function PhonemesFromText
423+-------------------------------------------------------------------------------}
424+function TSpeechSynthesizer.PhonemesFromText(stringText: String): String;
425+begin
426+ Result := NSStringToString(SS.phonemesFromText(NSStr(stringText)));
427+end;
428+
429+{-------------------------------------------------------------------------------
430+ procedure AllocDelegate
431+-------------------------------------------------------------------------------}
432+procedure TSpeechSynthesizer.AllocDelegate;
433+begin
434+ Del := TSpeechDelegate.alloc.init;
435+ Del.Obj:=Self;
436+ SS.setDelegate(Del);
437+end;
438+
439+{-------------------------------------------------------------------------------
440+ procedure SetRate
441+-------------------------------------------------------------------------------}
442+procedure TSpeechSynthesizer.DoFinishedSpeaking;
443+begin
444+ if Assigned(fOnFinish) then fOnFinish(Self);
445+end;
446+
447+{-------------------------------------------------------------------------------
448+ constructor Create
449+-------------------------------------------------------------------------------}
450+constructor TSpeechSynthesizer.Create;
451+begin
452+ inherited;
453+ SS := NSSpeechSynthesizer.alloc.init;
454+ AllocDelegate;
455+end;
456+
457+constructor TSpeechSynthesizer.Create(stringVoice: String);
458+begin
459+ SS := NSSpeechSynthesizer.alloc.initWithVoice(NSStr(stringVoice));
460+ AllocDelegate;
461+end;
462+
463+destructor TSpeechSynthesizer.Destroy;
464+begin
465+ Del.release;
466+ SS.release;
467+ inherited Destroy;
468+end;
469+
470+end.
471+```
472+
473+Example code:
474+
475+``` pascal
476+unit Unit1;
477+
478+{$mode objfpc}{$H+}
479+
480+interface
481+
482+uses
483+ Classes, Forms, StdCtrls, Menus, speechsynthesize;
484+
485+type
486+
487+ { TForm1 }
488+
489+ TForm1 = class(TForm)
490+ MainMenu1: TMainMenu;
491+ Memo1: TMemo;
492+ MenuItemEdit: TMenuItem;
493+ MenuItemEditSpeech: TMenuItem;
494+ MenuItemEditSpeechStart: TMenuItem;
495+ MenuItemEditSpeechStop: TMenuItem;
496+ procedure FormCreate(Sender: TObject);
497+ procedure FormDestroy(Sender: TObject);
498+ procedure MenuItemEditSpeechStartClick(Sender: TObject);
499+ procedure MenuItemEditSpeechStopClick(Sender: TObject);
500+ private
501+ { private declarations }
502+ SpeechSynthesizer: TSpeechSynthesizer;
503+ public
504+ { public declarations }
505+ procedure OnFinish(Sender: TObject);
506+ end;
507+
508+var
509+ Form1: TForm1;
510+
511+implementation
512+
513+{$R *.lfm}
514+
515+{ TForm1 }
516+
517+procedure TForm1.FormCreate(Sender: TObject);
518+begin
519+ SpeechSynthesizer := TSpeechSynthesizer.Create;
520+
521+ // SpeechSynthesizer.OnFinish <-@OnFinish (Self)
522+ SpeechSynthesizer.OnFinish := @OnFinish;
523+
524+ MenuItemEditSpeechStop.Enabled := False;
525+end;
526+
527+procedure TForm1.FormDestroy(Sender: TObject);
528+begin
529+ SpeechSynthesizer.Free;
530+end;
531+
532+procedure TForm1.MenuItemEditSpeechStartClick(Sender: TObject);
533+begin
534+ // IsSpeaking - yes -> StopSpeaking
535+ if SpeechSynthesizer.IsSpeaking then
536+ begin
537+ SpeechSynthesizer.StopSpeaking;
538+ MenuItemEditSpeechStop.Enabled := False;
539+ end;
540+
541+ // StartSpeakingString
542+ SpeechSynthesizer.StartSpeakingString(Memo1.Text);
543+
544+ MenuItemEditSpeechStop.Enabled := True;
545+end;
546+
547+procedure TForm1.MenuItemEditSpeechStopClick(Sender: TObject);
548+begin
549+ SpeechSynthesizer.StopSpeaking;
550+end;
551+
552+{ OnFinish }
553+
554+procedure TForm1.OnFinish(Sender: TObject);
555+begin
556+ // not SpeechSynthesizer.IsSpeaking - yes or no
557+ if not SpeechSynthesizer.IsSpeaking then
558+ begin
559+ MenuItemEditSpeechStart.Enabled := True;
560+ MenuItemEditSpeechStop.Enabled := False;
561+ end;
562+end;
563+
564+end.
565+```
566+
567+For more details of the NSSpeechSynthesizer object's functionality, see the Apple documentation below.
568+
569+### Using AVSpeechSynthesis
570+
571+The AVSpeechSynthesizer class produces synthesized speech from text and provides methods for controlling or monitoring the progress of ongoing speech. To speak some text, you must first create an AVSpeechUtterance instance containing the text. (Optionally, you may also use the utterance object to control parameters affecting its speech, such as voice, pitch, and rate.) Then, pass it to the speakUtterance method on a speech synthesizer instance to speak that utterance. By default, AVSpeechSynthesizer will speak using a voice based on the user’s current language preferences.
572+
573+The speech synthesizer maintains a queue of utterances to be spoken. If the synthesizer is not currently speaking, calling speakUtterance: begins speaking that utterance immediately (or begin waiting through its preUtteranceDelay if one is set). If the synthesizer is speaking, utterances are added to a queue and spoken in the order they are received.
574+
575+After speech has begun, you can use the synthesizer object to pause or stop speech. After speech is paused, it may be continued from the point at which it left off; stopping ends speech entirely, removing any utterances yet to be spoken from the synthesizer’s queue. You may monitor the speech synthesizer by examining its speaking and paused properties, or by setting a delegate. Messages in the AVSpeechSynthesizerDelegate protocol are sent as significant events occur during speech synthesis.
576+
577+## Generating speech using utilities
578+
579+### Using /usr/bin/say
580+
581+This tool uses the Speech Synthesis manager to convert input text to audible speech and either play it through the sound output device chosen in System Preferences or save it to an AIFF file.
582+
583+``` pascal
584+...
585+
586+Uses
587+ Unix,
588+ BaseUnix,
589+ ...;
590+
591+...
592+
593+procedure TForm1.MenuItem22Click(Sender: TObject);
594+var
595+ status: LongInt;
596+ tts: String;
597+begin
598+ tts := 'Hello, this is a test.';
599+ status := fpSystem('/usr/bin/say "' + tts + '"');
600+ ShowMessage('Exit status: ' + IntToStr(wexitStatus(status)));
601+end;
602+```
603+
604+``` pascal
605+Program sayhello;
606+
607+Uses
608+ Process;
609+
610+Var
611+ AProcess:TProcess;
612+
613+Begin
614+ AProcess := TProcess.Create(nil);
615+ Try
616+ AProcess.Executable := '/usr/bin/say';
617+ AProcess.Parameters.Add( '-o');
618+ AProcess.Parameters.Add( '/Users/<you>/Desktop/hello.aiff'); // save speech to file on your desktop
619+ AProcess.Parameters.Add( 'hello');
620+ AProcess.Options := AProcess.Options + [poWaitOnExit];
621+ AProcess.Execute;
622+ Finally
623+ AProcess.Free;
624+ End;
625+End.
626+```
627+
628+For more details on how to use this command, open a Terminal and type `man say` for the manual page.
629+
630+### Using osascript
631+
632+The `osascript` command line utility executes the given OSA script, which may be plain text or a compiled script (.scpt) created by the Applications \> Utilities \> Script Editor application or the `osacompile` command line utility. By default, `osascript` treats plain text as AppleScript, but you can change this using the -l option to JavaScript or the Generic Scripting System. To get a list of the OSA languages installed on your system, run the `osalang` command line utility.
633+
634+Example:
635+
636+``` pascal
637+//
638+// From a forum post by jwdietrich:
639+//
640+
641+var
642+ s: longint;
643+ t: String;
644+begin
645+ t := 'This is a test.';
646+ s := fpSystem('osascript -e ''say "' + t + '"''');
647+end;
648+```
649+
650+For more details on how to use this command, open a Terminal and type `man osascript` for the manual page.
651+
652+## See also
653+
654+- [[LCL Accessibility|Using the macOS screenreader]]
655+- [[Speech Synthesis]] Details of cross-platform and native operating system solutions.
656+
657+## External links
658+
659+- [Apple: Speech Synthesis](https://developer.apple.com/documentation/avfoundation/speech_synthesis).
660+- [Apple: Speech Manager/Speech Synthesis Manager](https://developer.apple.com/library/archive/documentation/mac/pdf/Sound/Speech_Manager.pdf) (PDF) Legacy Pascal code; macOS 10.0+ (with deprecations).
661+- [Apple: NSSpeechSynthesizer](https://developer.apple.com/documentation/appkit/nsspeechsynthesizer) macOS 10.3+.
662+- [Apple: AVSpeechSynthesizer](https://developer.apple.com/documentation/avfoundation/avspeechsynthesizer) macOS 10.14+.
663+
664+---
665+*From the Free Pascal wiki, page "macOS Text-To-Speech", as it stood on 2020-06-04 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history