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


    切分语句

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

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

    打开 SupportingFilesWhirlySquirrelly.plist ,其内容例如以下:

    你还能够通过右键->“Open AsSource 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. 设置要显示的文本和背景图片。

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


  • 相关阅读:
    [Linux]调整swap
    [Linux]mysql错误总结-ERROR 1067 (42000): Invalid default value for TIMESTAMP
    Canvas动画:地球绕着太阳转
    50个好用的前端框架,建议收藏!
    flex布局属性说明
    纯CSS绘制的图形一览
    深入理解CSS盒模型(转)
    JS的防抖与节流学习笔记
    应用八:Vue之在nginx下的部署实践
    css元素居中的几种方式
  • 原文地址:https://www.cnblogs.com/yxwkf/p/3862206.html
Copyright © 2020-2023  润新知