ios电子书语音阅读

非常感谢大家利用自己宝贵的时间来阅读我的文章 ,  上一篇文章写了Epub的加密一个实现方式,《ios Epub加密及解密阅读的一种实现方式》,今天这篇文章主要说一下电子书的语音阅读,这个功能也基本上是阅读器的标配了。希望这篇文章能给你的开发过程带来一些帮助。喜欢的可以关注一下我的简书我的博客   

demo下载地址ZQReaderDemo

关于电子书阅读这这块就不说了,还是在XDSReader的基础上修改,添加了语音阅读的功能

在网上查了些资料,IOS7.0之后系统添加了文字转语音的API,AVFoundation框架下的AVSpeechSynthesizer类,具体使用代码如下,也可参考ZQReaderDemo中的ZQAVSpeechTool

//初始化语音合成器  

_avSpeaker = [[AVSpeechSynthesizer alloc] init];  

_avSpeaker.delegate = self;  

//初始化要说出的内容  

AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:_paragraphs[_currentParagraphs]];  

//设置语速,语速介于AVSpeechUtteranceMaximumSpeechRate和AVSpeechUtteranceMinimumSpeechRate之间  

//AVSpeechUtteranceMaximumSpeechRate  

//AVSpeechUtteranceMinimumSpeechRate  

//AVSpeechUtteranceDefaultSpeechRate  

utterance.rate = _rate;  


//设置音高,[0.5 - 2] 默认 = 1  

//AVSpeechUtteranceMaximumSpeechRate  

//AVSpeechUtteranceMinimumSpeechRate  

//AVSpeechUtteranceDefaultSpeechRate  

utterance.pitchMultiplier = 1;  


//设置音量,[0-1] 默认 = 1  

utterance.volume = 1;  


//读一段前的停顿时间  

utterance.preUtteranceDelay = 0.5;  

//读完一段后的停顿时间  

utterance.postUtteranceDelay = 0;  


//设置声音,是AVSpeechSynthesisVoice对象  

//AVSpeechSynthesisVoice定义了一系列的声音, 主要是不同的语言和地区.  

//voiceWithLanguage: 根据制定的语言, 获得一个声音.  

//speechVoices: 获得当前设备支持的声音  

//currentLanguageCode: 获得当前声音的语言字符串, 比如”ZH-cn”  

//language: 获得当前的语言  

//通过特定的语言获得声音  

AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];  

//通过voicce标示获得声音  

//AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithIdentifier:AVSpeechSynthesisVoiceIdentifierAlex];  

utterance.voice = voice;  

//开始朗读  

[_avSpeaker speakUtterance:utterance];  

既然语音转文字实现方法找到了,那么剩下的都是逻辑上的问题了,最后整理了一下实现思路,用户在阅读菜单上点击语音按钮,开始语音阅读当前页内容,首先取出当前页的所有文本,当前页读完跳转下一页继续阅读,后考虑到高亮问题,又把每一页的文本拆分成段落进行阅读,当读完一段开始读下一段,当前页读完就继续读下一页,当前章读完就读下一章,整体思路想好了就开整。

1、创建了一个单例工具类ZQAVSpeechTool,代码如下

.h

#import   


@interface ZQAVSpeechTool : NSObject  


+(instancetype)shareSpeechTool;  


//开始朗读  

- (void)speechTextWith:(NSString *)text;  

//暂停朗读  

- (void)pauseSpeech;  

//继续朗读  

- (void)continueSpeech;  

//结束朗读  

- (void)StopSpeech;  

//切换语速  

- (void)changeRate:(CGFloat)rate;  



@end  

.m

#import "ZQAVSpeechTool.h"  

#import   


@interface ZQAVSpeechTool ()  


@property (nonatomic ,strong)AVSpeechSynthesizer *avSpeaker;  


@property (nonatomic ,strong)NSArray *paragraphs;  


@property (nonatomic ,assign)NSInteger currentParagraphs;  



@property (nonatomic ,assign)CGFloat rate;  



@end  


@implementation ZQAVSpeechTool  


// 单例  

+(instancetype)shareSpeechTool {  

static ZQAVSpeechTool *instance;  

static dispatch_once_t onceToken;  

    dispatch_once(&onceToken, ^{  

instance = [[ZQAVSpeechTool alloc]init];  


    });  

return instance;  

}  


- (void)speechTextWith:(NSString *)text  

{  

if (!(text.length>0)) {  

return;  

    }  

if (_avSpeaker) {  

//把每一页文字拆分成段  

_paragraphs =[text componentsSeparatedByString:@"\n"];  

_currentParagraphs =0;  

[self speechParagraphWith:_paragraphs[_currentParagraphs]];  

}else  

    {  

//初次阅读  

NSUserDefaults *useDef = [NSUserDefaults standardUserDefaults];  

_rate = [useDef floatForKey:@"speechRate"];  

if (!_rate) {  

_rate =0.5;  

        }  

_paragraphs =[text componentsSeparatedByString:@"\n"];  

_currentParagraphs =0;  

[[NSNotificationCenter defaultCenter] postNotificationName:@"speechParagraph" object:_paragraphs[_currentParagraphs]];  

//初始化语音合成器  

_avSpeaker = [[AVSpeechSynthesizer alloc] init];  

_avSpeaker.delegate = self;  

//初始化要说出的内容  

AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:_paragraphs[_currentParagraphs]];  

//设置语速,语速介于AVSpeechUtteranceMaximumSpeechRate和AVSpeechUtteranceMinimumSpeechRate之间  

//AVSpeechUtteranceMaximumSpeechRate  

//AVSpeechUtteranceMinimumSpeechRate  

//AVSpeechUtteranceDefaultSpeechRate  

utterance.rate = _rate;  


//设置音高,[0.5 - 2] 默认 = 1  

//AVSpeechUtteranceMaximumSpeechRate  

//AVSpeechUtteranceMinimumSpeechRate  

//AVSpeechUtteranceDefaultSpeechRate  

utterance.pitchMultiplier = 1;  


//设置音量,[0-1] 默认 = 1  

utterance.volume = 1;  


//读一段前的停顿时间  

utterance.preUtteranceDelay = 0.5;  

//读完一段后的停顿时间  

utterance.postUtteranceDelay = 0;  


//设置声音,是AVSpeechSynthesisVoice对象  

//AVSpeechSynthesisVoice定义了一系列的声音, 主要是不同的语言和地区.  

//voiceWithLanguage: 根据制定的语言, 获得一个声音.  

//speechVoices: 获得当前设备支持的声音  

//currentLanguageCode: 获得当前声音的语言字符串, 比如”ZH-cn”  

//language: 获得当前的语言  

//通过特定的语言获得声音  

AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"];  

//通过voicce标示获得声音  

//AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithIdentifier:AVSpeechSynthesisVoiceIdentifierAlex];  

utterance.voice = voice;  

//开始朗读  

[_avSpeaker speakUtterance:utterance];  

    }  


}  


- (void)speechParagraphWith:(NSString *)Paragraph  

{  

[[NSNotificationCenter defaultCenter] postNotificationName:@"speechParagraph" object:Paragraph];  

AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:Paragraph];  

utterance.rate = _rate;  

[_avSpeaker speakUtterance:utterance];  

}  

//切换语速  

- (void)changeRate:(CGFloat)rate  

{  

    _rate = rate;  

//  

[_avSpeaker stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];    //初始化语音合成器  

//    _avSpeaker = [[AVSpeechSynthesizer alloc] init];  

//    _avSpeaker.delegate = self;  

AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:_paragraphs[_currentParagraphs]];  

utterance.rate = rate;  

utterance.pitchMultiplier = 1;  

utterance.volume = 1;  

utterance.preUtteranceDelay = 0.5;  

utterance.postUtteranceDelay = 0;  



[_avSpeaker speakUtterance:utterance];  

NSUserDefaults *useDef = [NSUserDefaults standardUserDefaults];  

[useDef setFloat:rate forKey:@"speechRate"];  

[useDef synchronize];  


}  


- (void)pauseSpeech  

{  

//暂停朗读  

//AVSpeechBoundaryImmediate 立即停止  

//AVSpeechBoundaryWord    当前词结束后停止  

[_avSpeaker pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];  

}  


- (void)continueSpeech  

{  

[_avSpeaker continueSpeaking];  


}  


- (void)StopSpeech  

{  

//AVSpeechBoundaryImmediate 立即停止  

//AVSpeechBoundaryWord    当前词结束后停止  

[_avSpeaker stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];  

_avSpeaker = nil;  

[XDSReadManager sharedManager].speeching = NO;  

[[NSNotificationCenter defaultCenter] postNotificationName:@"speechDidStop" object:nil];  


}  


#pragma mark -  

#pragma mark - AVSpeechSynthesizerDelegate  

//已经开始  

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didStartSpeechUtterance:(AVSpeechUtterance *)utterance{  



}  

//已经说完  

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance{  




_currentParagraphs+=1;  

if (_currentParagraphs<_paragraphs.count) {  

//读下一段  

[self speechParagraphWith:_paragraphs[_currentParagraphs]];  

}else{  



NSInteger currentPage = CURRENT_RECORD.currentPage;  

NSInteger currentChapter = CURRENT_RECORD.currentChapter;  

if (currentPage < CURRENT_RECORD.totalPage - 1) {  

//下一页  

currentPage +=1;  

}else  

    {  

if (currentChapter < CURRENT_RECORD.totalChapters - 1) {  

//下一章  

currentChapter +=1;  

currentPage =0;  

}else  

        {  

//全书读完  

[self StopSpeech];  

return;  

        }  


    }  


[[XDSReadManager sharedManager] readViewJumpToChapter:currentChapter page:currentPage];  

NSString *content = CURRENT_RECORD.chapterModel.pageStrings[currentPage];  

[self speechTextWith:content];  


    }  


}  




//已经暂停  

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didPauseSpeechUtterance:(AVSpeechUtterance *)utterance{  


}  

//已经继续说话  

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didContinueSpeechUtterance:(AVSpeechUtterance *)utterance{  


}  

//已经取消说话  

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didCancelSpeechUtterance:(AVSpeechUtterance *)utterance{  


}  

//将要说某段话  

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer willSpeakRangeOfSpeechString:(NSRange)characterRange utterance:(AVSpeechUtterance *)utterance{  


//    DebugLog(@"%@",utterance.speechString);  


}  


@end  

2、为XDSReadManager添加开始语音阅读的方法,并添加一个用于判断当前阅读状态的属性

- (void)begainSpeech;  


@property (nonatomic ,assign)BOOL speeching;  

方法的实现

//开始语音阅读  

- (void)begainSpeech  

{  

//取出当前页文字内容  

XDSChapterModel *currentChapterModel = _bookModel.record.chapterModel;  

NSInteger currentPage = _bookModel.record.currentPage;  

NSString *content = currentChapterModel.pageStrings[currentPage];  

//开始朗读本页文字  

[[ZQAVSpeechTool shareSpeechTool] speechTextWith:content];  

self.speeching = YES;  


}  

3、在弹出菜单时判断是否正在朗读,是则弹出语音阅读菜单ZQSpeechMenuView,因为AVSpeechSynthesizer只有女声,所以菜单只是加了调节语速和暂停/播放、退出的功能,没有添加切换男女声的功能

主要功能实现


//暂停/播放  

- (void)pauseBtnClick:(UIButton *)sender  

{  

if (_pauseBtn.selected) {  

[[ZQAVSpeechTool shareSpeechTool] continueSpeech];  

}else{  

[[ZQAVSpeechTool shareSpeechTool] pauseSpeech];  

    }  

_pauseBtn.selected = !_pauseBtn.selected;  

}  


//退出  

- (void)stopBtnClick  

{  

_pauseBtn.selected = NO;  

[[ZQAVSpeechTool shareSpeechTool] StopSpeech];  

[self removeFromSuperview];  

}  


//切换语速  

- (void)sliderValueChanged:(UISlider *)slider{  


[[ZQAVSpeechTool shareSpeechTool] changeRate:slider.value];  

_pauseBtn.selected = NO;  

}  

4、高亮正在阅读段落

- (void)highlightTextWith:(NSString *)text{  


//移除上一段背景色  

if (_currentRange.length>0) {  

[_readAttributedContent removeAttribute:NSBackgroundColorAttributeName range:_currentRange];  

    }  

//设置当前朗读段落背景色  

NSRange range = [_content rangeOfString:text];  

    _currentRange = range;  

[_readAttributedContent addAttribute:NSBackgroundColorAttributeName value:RGB(150, 220, 240) range:range];  

self.readAttributedContent = _readAttributedContent;  

[self reloadView];  

}  

大工搞成,具体的实现逻辑及代码见ZQReaderDemo,语音菜单界面约束使用的SDAutoLayout,可以根据自己习惯进行更改。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容