#31 使用 AVSpeechSynthesizer 我們一起學兔子叫

Rose
彼得潘的 Swift iOS / Flutter App 開發教室
17 min readApr 22, 2021

用 AVSpeechSynthesizer 做出進階的語音告白與動物叫聲的 APP

初始 APP 規劃

這個作業原本是設計講笑話的APP,會加入下列功能(但最後換成別的內容)

  1. 唸出 text field 輸入的文字。
  2. 利用 slider 控制講話的速度。
  3. 利用 slider 控制講話的音調。
  4. 利用 label 顯示 slider 的數值。
  5. 利用 slider 控制講話的音量。
  6. 加入多個講話的 button。

講笑話的APP 的版面規劃

但是因為規劃為三個頁面,控制播放的元件是放在第三頁

如果要拉線開雙視窗,右邊那頁開出來的頁面,不是我們要的 ViewController.swift 那頁

請教彼得潘後有給作法,課程還沒教到多頁面拉線的方法,但是因為看起來有點複雜還是先留著以後教到多頁面再做

彼得潘給的資料 做多畫面時 作法參考

新的畫面類別會是 UIViewControler ,要改的話要自訂類別,因為目前我們只教到一個畫面,所以如果做多個畫面會比較難

APP 重新設計 一起學兔子叫:

重新設計單頁面的APP,修修改改加上之前笑話 APP 做到這個 APP 已經是第四版了
加上自己很龜毛,畫面設計一定要找可愛的插圖,所以花了點時間
內容參考自己之前做過的作業以及同學的作業

之前寫的簡單版講話機器人:

規劃二個區塊為

  1. 任意輸入文字講話
  2. 點擊動物圖案講話:(有三種發音) 叫聲、動物中文名、動物英文名

Xcode 排版完的畫面

在 storyboard 做小動物們的排版有點小技巧,遇到這種一個區塊很多物件的設計時,最好開一個 View 把他們都包進去
這樣如果要移動位置、調整座標時,可以只移動外層的 View 就好,不用每隻動物都要移動

連結元件與程式,拉 Outlet

完成第一部分任意輸入文字,由APP播放語音

語音必須要載入 AVFoundation

import UIKit
import AVFoundation
class ViewController: UIViewController {@IBAction func changeSlider(_ sender: Any) {
speakRateText.text = String(format: "%.2f", speakRate.value)
speakMultiplierText.text = String(format: "%.2f", speakMultiplier.value)
}

@IBOutlet weak var sayTextField: UITextField!
@IBOutlet weak var speakMultiplierText: UILabel!
@IBOutlet weak var speakRateText: UILabel!
@IBOutlet weak var speakMultiplier: UISlider!
@IBOutlet weak var speakRate: UISlider!

@IBAction func SpeakButton(_ sender: UIButton) {
let utterance = AVSpeechUtterance(string: sayTextField.text!)
let synthesizer = AVSpeechSynthesizer()
utterance.voice = AVSpeechSynthesisVoice(language: "zh-TW")
utterance.rate = speakRate.value
utterance.pitchMultiplier = speakMultiplier.value
synthesizer.speak(utterance)
}

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}

動物按鈕分別拉 Outlet

Button 命名:拉 Outlet 之前都一樣叫 Button,拉過 Outlet 之後會依照給元件的命名呈現

拉 Outlet 連結畫面跟程式,可以從 Storyboard 上的元件按下 Control + 滑鼠拉線,但最好是從左側的列表拉,比較不會跑掉或拉動到元件的位置

動物的叫聲

在英文裡的擬聲詞

🐰 兔子 mumble
🐱 貓咪 meow
🐶 狗狗 woof
🐣 小雞 cluck
🐒 猴子 screech, gibber
🦁 獅子 roar
🐻 小熊 growl
🐯 老虎 roar, howl
🐷 小豬 oink
🐘 大象 pawoo
🐮 母牛 moo
🐦 小鳥 tweet tweet

把APP安裝到手機上測

安裝到手機的操作方式與錯誤處理

手機截圖

這邊還有個問題要處理,上方任意輸入文字完成後,鍵盤要收起來才不會擋到下面的小動物們

點擊畫面空白處讓鍵盤收起

override func viewDidLoad() {
super.viewDidLoad()

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyBoard))
self.view.addGestureRecognizer(tap) // to Replace "TouchesBegan"
}
@objc func dismissKeyBoard() {
self.view.endEditing(true)
}

操作影片錄影

語音輸入「王老先生有塊地」結果辨識成「王老先生有快遞」
其實有快遞也是很合理的😂

完整程式碼

import UIKit
import AVFoundation
class ViewController: UIViewController {

// 輸入文字、音調速度
@IBOutlet weak var sayTextField: UITextField!
@IBOutlet weak var speakMultiplierText: UILabel!
@IBOutlet weak var speakRateText: UILabel!
@IBOutlet weak var speakMultiplier: UISlider!
@IBOutlet weak var speakRate: UISlider!
// 播放告白
@IBOutlet weak var talkButton: UIButton!
// 叫聲、中英文
@IBOutlet weak var languageSegment: UISegmentedControl!


// 動物按鈕
@IBOutlet weak var rabbitBtn: UIButton!
@IBOutlet weak var chickBtn: UIButton!
@IBOutlet weak var bearBtn: UIButton!
@IBOutlet weak var elephantBtn: UIButton!
@IBOutlet weak var cowBtn: UIButton!
@IBOutlet weak var lionBtn: UIButton!
@IBOutlet weak var tigerBtn: UIButton!
@IBOutlet weak var monkeyBtn: UIButton!
@IBOutlet weak var dogBtn: UIButton!
@IBOutlet weak var catBtn: UIButton!
@IBOutlet weak var pigBtn: UIButton!
@IBOutlet weak var birdBtn: UIButton!

// 音調速度
@IBAction func changeSlider(_ sender: Any) {
speakRateText.text = String(format: "%.2f", speakRate.value)
speakMultiplierText.text = String(format: "%.2f", speakMultiplier.value)
}

// 告白按鈕
@IBAction func SpeakButton(_ sender: UIButton) {
let utterance = AVSpeechUtterance(string: sayTextField.text!)
let synthesizer = AVSpeechSynthesizer()
utterance.voice = AVSpeechSynthesisVoice(language: "zh-TW")
utterance.rate = speakRate.value
utterance.pitchMultiplier = speakMultiplier.value
synthesizer.speak(utterance)
}

// 動物名稱的觸發,是點選按鈕發聲所以不用做segment的func
@IBAction func animalTalk(_ sender: UIButton) {
var animalmessage = AVSpeechUtterance()
// 用if做中文選項後做動物按鈕定義
if languageSegment.selectedSegmentIndex == 0{

if sender == catBtn{
animalmessage = AVSpeechUtterance(string: "貓咪")
}
if sender == rabbitBtn{
animalmessage = AVSpeechUtterance(string: "兔子")
}
if sender == dogBtn {
animalmessage = AVSpeechUtterance(string: "狗狗")
}
if sender == chickBtn{
animalmessage = AVSpeechUtterance(string: "小雞")
}
if sender == monkeyBtn{
animalmessage = AVSpeechUtterance(string: "猴子")
}
if sender == lionBtn{
animalmessage = AVSpeechUtterance(string: "獅子")
}
if sender == pigBtn{
animalmessage = AVSpeechUtterance(string: "小豬")
}
if sender == bearBtn{
animalmessage = AVSpeechUtterance(string: "小熊")
}
if sender == tigerBtn{
animalmessage = AVSpeechUtterance(string: "老虎")
}
if sender == elephantBtn{
animalmessage = AVSpeechUtterance(string: "大象")
}
if sender == cowBtn{
animalmessage = AVSpeechUtterance(string: "牛")
}
if sender == birdBtn{
animalmessage = AVSpeechUtterance(string: "小鳥")
}
// 語言設定一定要放發音後面
animalmessage.voice = AVSpeechSynthesisVoice(language: "zh-TW")
}
// 接著做英文發音
else if languageSegment.selectedSegmentIndex == 1{

if sender == catBtn{
animalmessage = AVSpeechUtterance(string: "cat")
}
if sender == rabbitBtn{
animalmessage = AVSpeechUtterance(string: "rabbit")
}
if sender == dogBtn {
animalmessage = AVSpeechUtterance(string: "dog")
}
if sender == chickBtn{
animalmessage = AVSpeechUtterance(string: "chick")
}
if sender == monkeyBtn{
animalmessage = AVSpeechUtterance(string: "monkey")
}
if sender == lionBtn{
animalmessage = AVSpeechUtterance(string: "lion")
}
if sender == pigBtn{
animalmessage = AVSpeechUtterance(string: "pig")
}
if sender == bearBtn{
animalmessage = AVSpeechUtterance(string: "bear")
}
if sender == tigerBtn{
animalmessage = AVSpeechUtterance(string: "tiger")
}
if sender == elephantBtn{
animalmessage = AVSpeechUtterance(string: "elephant")
}
if sender == cowBtn{
animalmessage = AVSpeechUtterance(string: "cow")
}
if sender == birdBtn{
animalmessage = AVSpeechUtterance(string: "bird")
}
animalmessage.voice = AVSpeechSynthesisVoice(language: "en-US")
}
// 叫聲
else{

if sender == catBtn{
animalmessage = AVSpeechUtterance(string: "meow meow meow")
}
if sender == rabbitBtn{
animalmessage = AVSpeechUtterance(string: "mumble mumble mumble")
}
if sender == dogBtn {
animalmessage = AVSpeechUtterance(string: "woof woof woof")
}
if sender == chickBtn{
animalmessage = AVSpeechUtterance(string: "cluck cluck cluck")
}
if sender == monkeyBtn{
animalmessage = AVSpeechUtterance(string: "gibber gibber gibber")
}
if sender == lionBtn{
animalmessage = AVSpeechUtterance(string: "roar roar roar")
}
if sender == pigBtn{
animalmessage = AVSpeechUtterance(string: "oink oink oink")
}
if sender == bearBtn{
animalmessage = AVSpeechUtterance(string: "growl growl growl")
}
if sender == tigerBtn{
animalmessage = AVSpeechUtterance(string: "howl howl howl")
}
if sender == elephantBtn{
animalmessage = AVSpeechUtterance(string: "pawoo pawoo pawoo")
}
if sender == cowBtn{
animalmessage = AVSpeechUtterance(string: "moo moo moo")
}
if sender == birdBtn{
animalmessage = AVSpeechUtterance(string: "tweet tweet tweet")
}
animalmessage.voice = AVSpeechSynthesisVoice(language: "en-US")
}
let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(animalmessage)

}

override func viewDidLoad() {
super.viewDidLoad()

let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyBoard))
self.view.addGestureRecognizer(tap) // to Replace "TouchesBegan"
}
@objc func dismissKeyBoard() {
self.view.endEditing(true)
}
}

--

--

Rose
彼得潘的 Swift iOS / Flutter App 開發教室

Coding & Design 一直在學習的路上,從未停止,一有空檔就會摸摸我的兔子🐰