怎样使用 iOS 7 的 AVSpeechSynthesizer 制作有声书(2)

怎样使用 iOS 7 的 AVSpeechSynthesizer 制作有声书(2)

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

切分语句

软件project的一条定律是数据和代码分离。这样做会使代码更易于測试,即使输入的数据发生改变,你的代码也能够同意。甚至于,程序能在执行中实时下载新的数据。假设程序能在执行中下载新书岂不是更好?

你如今用的书是用 Book.testBook 方法中的代码创建的。接下来我们将书改为以文件形式存储,读取的时候则通过Plist 文件来读取。

打开 SupportingFiles\WhirlySquirrelly.plist ,其内容例如以下:

怎样使用 iOS 7 的 AVSpeechSynthesizer 制作有声书(2)

你还能够通过右键->“Open As\Source Code”来查看其源代码:

<?xmlversion=“1.0”encoding=“UTF-8”?>

<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>

<plistversion=“1.0”>

 <dict>

   <key>bookPages</key>

   <array>

     <!– First page –>

     <dict>

       <key>backgroundImage</key>

       <string>PageBackgroundImage.jpg</string>

       <key>utterances</key>

       <array>

         <dict>

           <key>utteranceProperties</key>

           <dict>

             <key>pitchMultiplier</key>

             <real>1</real>

             <key>rate</key>

             <real>1.2</real>

           </dict>

           <key>utteranceString</key>

           <string>Whisky,</string>

         </dict>

         …

       </array>

     </dict>

     <!– Second page –>

     <dict>

       <key>backgroundImage</key>

       <string>PageBackgroundImage.jpg</string>

       <key>utterances</key>

       <array>

         <dict>

           <key>utteranceProperties</key>

           <dict>

 

             <key>pitchMultiplier</key>

             <real>1.2</real>

             <key>rate</key>

             <real>1.3</real>

           </dict>

           <key>utteranceString</key>

           <string>Whirly,</string>

         </dict>

         …

       </array>

     </dict>

   </array>

 </dict>

</plist>

它的数据结构用一个抽象的表示则例如以下图所看到的(这里{}代表字典,[]代表数组):

Book {

  bookPages => [

  {FirstPage

                backgroundImage => “Name ofbackground image file”,

  utterances => [

  { utteranceString     => “what to say first”,

    utteranceProperties => { how to say it }

  },

  { utteranceString     => “what to say next”,

    utteranceProperties => { how to say it }

  }

  ]

  },

  {SecondPage

                backgroundImage => “Name ofbackground image file”,

  utterances => [

  { utteranceString     => “what to say last”,

    utteranceProperties => { how to say it }

  }

  ]

  }

  ]

}

感谢伟大的 ASCII 艺术!:]

WhirlySquirrelly.plist将文本依照一个单词一个utterance 的方式进行切。这样做的优点是你能够控制每一个词的音高(高音、低音)和语速(快、慢)。之所以合成的语音太机械,就像一部上世纪50年代的低劣科幻电影,是由于他的发音太呆板了。为了使合成语音更接近于人,必须控制音高和语速,使其更富于变化。

解析plist

我们须要将WhirlySquirrelly 解析成 RWTBook 对象。打开RWTBook.h 在 bookWithPages:方法之后添�:

  + (instancetype)bookWithContentsOfFile:(NSString*)path;

这种方法会读取 WhirlySquirrelly.plist文件,然后依据文件内容返回一个  RWTBook实例。

打开 RWTBook.mand 在 #import “RWTPage.h” 以下添�:

#pragma mark –

External Constants   NSString* const RWTBookAttributesKeyBookPages = @“bookPages”;

这个常量是一个键名,用于从 plist 文件里检索图书的全部页数据。

RWTBook.m 在@end 之前添�:

#pragma mark – Private  

+ (instancetype)bookWithContentsOfFile:(NSString*)path {

   // 1

   NSDictionary *bookAttributes = [NSDictionary dictionaryWithContentsOfFile:path];

   if (!bookAttributes) {

     return nil;

   }

     // 2

   NSMutableArray *pages = [NSMutableArray arrayWithCapacity:2];

   for (NSDictionary *pageAttributes in [bookAttributes objectForKey:RWTBookAttributesKeyBookPages]) {

     RWTPage *page = [RWTPage pageWithAttributes:pageAttributes];

     if (page) {

       [pages addObject:page];

     }

   }

    // 3

   return [self bookWithPages:pages];

}

以上代码负责以下工作:

  1. 从给定的文件里读取并初始化了一个 NSDictionary 对象。这个文件就是WhirlySquirrelly.plist。
  2. 遍历字典中的 bookPages 数组,将数组中每一个元素解析为 Page 对象。
  3. 通过 bookWithPages 方法返回一个全新的 book 对象。

打开 RWTPageViewController.mand navigate ,在 viewDidLoad找到这一行:

  [self setupBook:[RWTBook testBook]];

将其替换为:

NSString *path = [[NSBundle mainBundle] pathForResource:@“WhirlySquirrelly” ofType:@“plist”];  

[self setupBook:[RWTBook bookWithContentsOfFile:path]];

这段代码将找到 WhirlySquirrelly.plist 的全路径,然后调用 bookWithContentsOfFile:创建 book 对象。

打开 RWTPage.m 在#import “RWTPage.h”之后添�:

@import AVFoundation;

如今你能够在文件里引用 AVSpeechUtterance 了。

在 RWTPageAttributesKeyBackgroundImage声明之后增�例如以下声明:

NSString* const RWTUtteranceAttributesKeyUtteranceString = @“utteranceString”;

NSString* const RWTUtteranceAttributesKeyUtteranceProperties = @“utteranceProperties”;

这些常量都是用于从 plist 中訪问每一个AVSpeechUtterance 的属性时要用到的。将

 pageWithAttributes:方法改动为:

+ (instancetype)pageWithAttributes:(NSDictionary*)attributes {

   RWTPage *page = [[RWTPage alloc] init];

     if ([[attributes objectForKey:RWTPageAttributesKeyUtterances] isKindOfClass:[NSString class]]) {

     // 1

     page.displayText = [attributes objectForKey:RWTPageAttributesKeyUtterances];

     page.backgroundImage = [attributes objectForKey:RWTPageAttributesKeyBackgroundImage];

   } else if ([[attributes objectForKey:RWTPageAttributesKeyUtterances] isKindOfClass:[NSArray class]]) {

     // 2

     NSMutableArray *utterances = [NSMutableArray arrayWithCapacity:31];

     NSMutableString *displayText = [NSMutableString stringWithCapacity:101];

     // 3

     for (NSDictionary *utteranceAttributes in [attributes objectForKey:RWTPageAttributesKeyUtterances]) {

       // 4

       NSString *utteranceString =                  [utteranceAttributes objectForKey:RWTUtteranceAttributesKeyUtteranceString];

       NSDictionary *utteranceProperties =                      [utteranceAttributes objectForKey:RWTUtteranceAttributesKeyUtteranceProperties];

         // 5

       AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:utteranceString];

       // 6

       [utterance setValuesForKeysWithDictionary:utteranceProperties];

       if (utterance) {

         // 7

         [utterances addObject:utterance];

         [displayText appendString:utteranceString];

       }

     }

       // 8

     page.displayText = displayText;

     page.backgroundImage = [UIImage imageNamed:[attributes objectForKey:RWTPageAttributesKeyBackgroundImage]];

   }

     return page;

}

这段代码负责:

  1. 处理 RWTBook.testBook 调用情况,这样的情况下,page 的 utterances 属性是一个 NSString。设置 displayText 和 backgroundImage 属性。
  2. 处理 book 数据来自 WhirlySquirrelly.plist  的情况,这样的情况下,page 的 utterances 是一个 NSArray 。将全部 utterances 和 display Text 合并。
  3. 遍历 page 中的每一个 utterances 。
  4. 读取每一个 utterances 的 utteranceString 和 utteranceProperties。
  5. 创建一个 AVSpeechUtterance 用于朗读 utteranceString。
  6. 通过键值编码(KVC)来改动 AVSpeechUtterance 实例属性。尽管苹果未在文档中说明,但能够调用 AVSpeechUtterance 的 setValuesForKeysWithDictionary:方法来设置全部 utteranceProtperties 属性。也就是说,你能够向 plist 中增�新的 utterance 属性,而不须要调用其 setter 方法,setValuesForKeysWithDictionary:方法会自己主动处理新属性。当然,在 AVSpeechUtterance 中对应的属性必须存在并且是可写的。
  7. 累加 utterance 并显示文本。
  8. 设置要显示的文本和背景图片。

编译执行,听听都在说些什么。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/118893.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 卸载Notepad++!事实已证明,它更牛逼……

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 作者:lucida lucida.me/blog/sublime-text-complete-guide/ 摘要(A…

    2021年6月26日
    134
  • stl merge函数使用

    stl merge函数使用merge函数的作用是:将两个已经排好序的序列合并为一个有序的序列。函数参数:merge(first1,last1,first2,last2,result,compare);firs1t为第一个容器的首迭代器,last1为第一个容器的末迭代器;first2为第二个容器的首迭代器,last2为容器的末迭代器;result为存放结果的容器,comapre为比较函数(可略写,默认为合并为…

    2022年6月8日
    40
  • python中list与string的转换「建议收藏」

    python中list与string的转换「建议收藏」1.list转string命令:”.join(list)其中,引号中是字符之间的分割符,如“,”,“;”,“\t”等等如:list=[1,2,3,4,5]”.join(list)结果即为:12345′,’.join(list)结果即为:1,2,3,4,5str=[]#有的题目要输出字符串,但是有时候list更好操作,于是可以最后list转st…

    2022年6月13日
    354
  • 《抓住听众心理——演讲者要知道的100件事》一第 1 章 人们是怎样思考和学习的…

    《抓住听众心理——演讲者要知道的100件事》一第 1 章 人们是怎样思考和学习的…本节书摘来异步社区《抓住听众心理——演讲者要知道的100件事》一书中的第1章,第1.1节,作者:【美】SusanM.Weinschenk译者:杨妩霞,杨煜泳责编:赵轩,更多章节内容可以访问云栖社区“异步社区”公众号查看。第1章 人们是怎样思考和学习的抓住听众心理——演讲者要知道的100件事“我从来没有‘教导’过我的学生;我只是尝…

    2022年9月12日
    0
  • Python基础之lambda表达式

    Python基础之lambda表达式目录1、lambda函数介绍2、lambda函数与def函数的区别3、lambda案例4、map方法混搭有时在使用函数时不需要给函数分配一个名称,该函数就是“匿名函数”。在python中使用lambda表达式表示匿名函数语法:lambda参数列表:lambda体lambda是关键字声明,在lambda表达式中,参数列表与函数中的参数列表一样,但不需要用小括号括起来,冒号后面是lambda体,lambda表达式的主要代码在lambda体处编写,类似于函数体。提示:lambda体不能是一个代码块,不能包含多条

    2022年10月17日
    0
  • ue4地编教程_编绳方法

    ue4地编教程_编绳方法一、基本操作二、导入模型和贴图##(一)导入UE4设置:1、导入:2、导入设置##(二)从3DMAX出设置##(三)从Blender导出

    2022年9月26日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号