您的位置:首页 > 移动开发 > IOS开发

如何使用 iOS 7 的 AVSpeechSynthesizer 制作有声书(2)

2014-07-11 16:00 411 查看
切分语句
软件工程的一条定律是数据和代码分离。这样做会使代码更易于测试,即使输入的数据发生改变,你的代码也能够允许。甚至于,程序能在运行中实时下载新的数据。如果程序能在运行中下载新书岂不是更好?
你现在用的书是用 Book.testBook 方法中的代码创建的。接下来我们将书改为以文件形式存储,读取的时候则通过Plist 文件来读取。
打开 SupportingFiles\WhirlySquirrelly.plist ,其内容如下:



你还可以通过右键->“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];
}
以上代码负责以下工作:
从给定的文件中读取并初始化了一个 NSDictionary 对象。这个文件就是WhirlySquirrelly.plist。
遍历字典中的 bookPages 数组,将数组中每个元素解析为 Page 对象。
通过 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;
}
这段代码负责:
处理 RWTBook.testBook 调用情况,这种情况下,page 的 utterances 属性是一个 NSString。设置 displayText 和 backgroundImage 属性。
处理 book 数据来自 WhirlySquirrelly.plist  的情况,这种情况下,page 的 utterances 是一个 NSArray 。将所有 utterances 和 display Text 合并。
遍历 page 中的每个 utterances 。
读取每个 utterances 的 utteranceString 和 utteranceProperties。
创建一个 AVSpeechUtterance 用于朗读 utteranceString。
通过键值编码(KVC)来修改 AVSpeechUtterance 实例属性。虽然苹果未在文档中说明,但可以调用 AVSpeechUtterance 的 setValuesForKeysWithDictionary:方法来设置所有 utteranceProtperties 属性。也就是说,你可以向 plist 中添加新的 utterance 属性,而不需要调用其 setter 方法,setValuesForKeysWithDictionary:方法会自动处理新属性。当然,在 AVSpeechUtterance 中相应的属性必须存在而且是可写的。
累加 utterance 并显示文本。
设置要显示的文本和背景图片。
编译运行,听听都在说些什么。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: