ios電子書(shū)語(yǔ)音閱讀

非常感謝大家利用自己寶貴的時(shí)間來(lái)閱讀我的文章 , ?上一篇文章寫(xiě)了Epub的加密一個(gè)實(shí)現(xiàn)方式,《ios Epub加密及解密閱讀的一種實(shí)現(xiàn)方式》,今天這篇文章主要說(shuō)一下電子書(shū)的語(yǔ)音閱讀叹洲,這個(gè)功能也基本上是閱讀器的標(biāo)配了离福。希望這篇文章能給你的開(kāi)發(fā)過(guò)程帶來(lái)一些幫助爬骤。喜歡的可以關(guān)注一下我的簡(jiǎn)書(shū)醋奠、我的博客 ??

demo下載地址ZQReaderDemo

關(guān)于電子書(shū)閱讀這這塊就不說(shuō)了,還是在XDSReader的基礎(chǔ)上修改耳峦,添加了語(yǔ)音閱讀的功能

在網(wǎng)上查了些資料恩静,IOS7.0之后系統(tǒng)添加了文字轉(zhuǎn)語(yǔ)音的API,AVFoundation框架下的AVSpeechSynthesizer類蹲坷,具體使用代碼如下驶乾,也可參考ZQReaderDemo中的ZQAVSpeechTool

//初始化語(yǔ)音合成器??

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

_avSpeaker.delegate?=?self;??

//初始化要說(shuō)出的內(nèi)容??

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

//設(shè)置語(yǔ)速,語(yǔ)速介于AVSpeechUtteranceMaximumSpeechRate和AVSpeechUtteranceMinimumSpeechRate之間??

//AVSpeechUtteranceMaximumSpeechRate??

//AVSpeechUtteranceMinimumSpeechRate??

//AVSpeechUtteranceDefaultSpeechRate??

utterance.rate?=?_rate;??


//設(shè)置音高,[0.5?-?2]?默認(rèn)?=?1??

//AVSpeechUtteranceMaximumSpeechRate??

//AVSpeechUtteranceMinimumSpeechRate??

//AVSpeechUtteranceDefaultSpeechRate??

utterance.pitchMultiplier?=?1;??


//設(shè)置音量,[0-1]?默認(rèn)?=?1??

utterance.volume?=?1;??


//讀一段前的停頓時(shí)間??

utterance.preUtteranceDelay?=?0.5;??

//讀完一段后的停頓時(shí)間??

utterance.postUtteranceDelay?=?0;??


//設(shè)置聲音,是AVSpeechSynthesisVoice對(duì)象??

//AVSpeechSynthesisVoice定義了一系列的聲音,?主要是不同的語(yǔ)言和地區(qū).??

//voiceWithLanguage:?根據(jù)制定的語(yǔ)言,?獲得一個(gè)聲音.??

//speechVoices:?獲得當(dāng)前設(shè)備支持的聲音??

//currentLanguageCode:?獲得當(dāng)前聲音的語(yǔ)言字符串,?比如”ZH-cn”??

//language:?獲得當(dāng)前的語(yǔ)言??

//通過(guò)特定的語(yǔ)言獲得聲音??

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

//通過(guò)voicce標(biāo)示獲得聲音??

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

utterance.voice?=?voice;??

//開(kāi)始朗讀??

[_avSpeaker?speakUtterance:utterance];??

既然語(yǔ)音轉(zhuǎn)文字實(shí)現(xiàn)方法找到了,那么剩下的都是邏輯上的問(wèn)題了循签,最后整理了一下實(shí)現(xiàn)思路级乐,用戶在閱讀菜單上點(diǎn)擊語(yǔ)音按鈕,開(kāi)始語(yǔ)音閱讀當(dāng)前頁(yè)內(nèi)容县匠,首先取出當(dāng)前頁(yè)的所有文本唇牧,當(dāng)前頁(yè)讀完跳轉(zhuǎn)下一頁(yè)繼續(xù)閱讀,后考慮到高亮問(wèn)題聚唐,又把每一頁(yè)的文本拆分成段落進(jìn)行閱讀,當(dāng)讀完一段開(kāi)始讀下一段腔召,當(dāng)前頁(yè)讀完就繼續(xù)讀下一頁(yè)杆查,當(dāng)前章讀完就讀下一章,整體思路想好了就開(kāi)整臀蛛。

1亲桦、創(chuàng)建了一個(gè)單例工具類ZQAVSpeechTool崖蜜,代碼如下

.h

#import???


@interface?ZQAVSpeechTool?:?NSObject??


+(instancetype)shareSpeechTool;??


//開(kāi)始朗讀??

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

//暫停朗讀??

-?(void)pauseSpeech;??

//繼續(xù)朗讀??

-?(void)continueSpeech;??

//結(jié)束朗讀??

-?(void)StopSpeech;??

//切換語(yǔ)速??

-?(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)?{??

//把每一頁(yè)文字拆分成段??

_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]];??

//初始化語(yǔ)音合成器??

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

_avSpeaker.delegate?=?self;??

//初始化要說(shuō)出的內(nèi)容??

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

//設(shè)置語(yǔ)速,語(yǔ)速介于AVSpeechUtteranceMaximumSpeechRate和AVSpeechUtteranceMinimumSpeechRate之間??

//AVSpeechUtteranceMaximumSpeechRate??

//AVSpeechUtteranceMinimumSpeechRate??

//AVSpeechUtteranceDefaultSpeechRate??

utterance.rate?=?_rate;??


//設(shè)置音高,[0.5?-?2]?默認(rèn)?=?1??

//AVSpeechUtteranceMaximumSpeechRate??

//AVSpeechUtteranceMinimumSpeechRate??

//AVSpeechUtteranceDefaultSpeechRate??

utterance.pitchMultiplier?=?1;??


//設(shè)置音量,[0-1]?默認(rèn)?=?1??

utterance.volume?=?1;??


//讀一段前的停頓時(shí)間??

utterance.preUtteranceDelay?=?0.5;??

//讀完一段后的停頓時(shí)間??

utterance.postUtteranceDelay?=?0;??


//設(shè)置聲音,是AVSpeechSynthesisVoice對(duì)象??

//AVSpeechSynthesisVoice定義了一系列的聲音,?主要是不同的語(yǔ)言和地區(qū).??

//voiceWithLanguage:?根據(jù)制定的語(yǔ)言,?獲得一個(gè)聲音.??

//speechVoices:?獲得當(dāng)前設(shè)備支持的聲音??

//currentLanguageCode:?獲得當(dāng)前聲音的語(yǔ)言字符串,?比如”ZH-cn”??

//language:?獲得當(dāng)前的語(yǔ)言??

//通過(guò)特定的語(yǔ)言獲得聲音??

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

//通過(guò)voicce標(biāo)示獲得聲音??

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

utterance.voice?=?voice;??

//開(kāi)始朗讀??

[_avSpeaker?speakUtterance:utterance];??

????}??


}??


-?(void)speechParagraphWith:(NSString?*)Paragraph??

{??

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

AVSpeechUtterance?*utterance?=?[AVSpeechUtterance?speechUtteranceWithString:Paragraph];??

utterance.rate?=?_rate;??

[_avSpeaker?speakUtterance:utterance];??

}??

//切換語(yǔ)速??

-?(void)changeRate:(CGFloat)rate??

{??

????_rate?=?rate;??

//??

[_avSpeaker?stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];????//初始化語(yǔ)音合成器??

//????_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????當(dāng)前詞結(jié)束后停止??

[_avSpeaker?pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];??

}??


-?(void)continueSpeech??

{??

[_avSpeaker?continueSpeaking];??


}??


-?(void)StopSpeech??

{??

//AVSpeechBoundaryImmediate?立即停止??

//AVSpeechBoundaryWord????當(dāng)前詞結(jié)束后停止??

[_avSpeaker?stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];??

_avSpeaker?=?nil;??

[XDSReadManager?sharedManager].speeching?=?NO;??

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


}??


#pragma?mark?-??

#pragma?mark?-?AVSpeechSynthesizerDelegate??

//已經(jīng)開(kāi)始??

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



}??

//已經(jīng)說(shuō)完??

-?(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)?{??

//下一頁(yè)??

currentPage?+=1;??

}else??

????{??

if?(currentChapter?<?CURRENT_RECORD.totalChapters?-?1)?{??

//下一章??

currentChapter?+=1;??

currentPage?=0;??

}else??

????????{??

//全書(shū)讀完??

[self?StopSpeech];??

return;??

????????}??


????}??


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

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

[self?speechTextWith:content];??


????}??


}??




//已經(jīng)暫停??

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


}??

//已經(jīng)繼續(xù)說(shuō)話??

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


}??

//已經(jīng)取消說(shuō)話??

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


}??

//將要說(shuō)某段話??

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


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


}??


@end??

2、為XDSReadManager添加開(kāi)始語(yǔ)音閱讀的方法客峭,并添加一個(gè)用于判斷當(dāng)前閱讀狀態(tài)的屬性

-?(void)begainSpeech;??


@property?(nonatomic?,assign)BOOL?speeching;??

方法的實(shí)現(xiàn)

//開(kāi)始語(yǔ)音閱讀??

-?(void)begainSpeech??

{??

//取出當(dāng)前頁(yè)文字內(nèi)容??

XDSChapterModel?*currentChapterModel?=?_bookModel.record.chapterModel;??

NSInteger?currentPage?=?_bookModel.record.currentPage;??

NSString?*content?=?currentChapterModel.pageStrings[currentPage];??

//開(kāi)始朗讀本頁(yè)文字??

[[ZQAVSpeechTool?shareSpeechTool]?speechTextWith:content];??

self.speeching?=?YES;??


}??

3豫领、在彈出菜單時(shí)判斷是否正在朗讀,是則彈出語(yǔ)音閱讀菜單ZQSpeechMenuView,因?yàn)锳VSpeechSynthesizer只有女聲舔琅,所以菜單只是加了調(diào)節(jié)語(yǔ)速和暫停/播放等恐、退出的功能,沒(méi)有添加切換男女聲的功能

主要功能實(shí)現(xiàn)


//暫停/播放??

-?(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];??

}??


//切換語(yǔ)速??

-?(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];??

????}??

//設(shè)置當(dāng)前朗讀段落背景色??

NSRange?range?=?[_content?rangeOfString:text];??

????_currentRange?=?range;??

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

self.readAttributedContent?=?_readAttributedContent;??

[self?reloadView];??

}??

大工搞成课蔬,具體的實(shí)現(xiàn)邏輯及代碼見(jiàn)ZQReaderDemo,語(yǔ)音菜單界面約束使用的SDAutoLayout郊尝,可以根據(jù)自己習(xí)慣進(jìn)行更改二跋。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市流昏,隨后出現(xiàn)的幾起案子扎即,更是在濱河造成了極大的恐慌,老刑警劉巖况凉,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谚鄙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡茎刚,警方通過(guò)查閱死者的電腦和手機(jī)襟锐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)膛锭,“玉大人粮坞,你說(shuō)我怎么就攤上這事〕跽” “怎么了莫杈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)奢入。 經(jīng)常有香客問(wèn)我筝闹,道長(zhǎng),這世上最難降的妖魔是什么腥光? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任关顷,我火速辦了婚禮,結(jié)果婚禮上武福,老公的妹妹穿的比我還像新娘议双。我一直安慰自己,他們只是感情好捉片,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布平痰。 她就那樣靜靜地躺著汞舱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宗雇。 梳的紋絲不亂的頭發(fā)上昂芜,一...
    開(kāi)封第一講書(shū)人閱讀 52,337評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音赔蒲,去河邊找鬼泌神。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嘹履,可吹牛的內(nèi)容都是我干的腻扇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼砾嫉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼幼苛!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起焕刮,我...
    開(kāi)封第一講書(shū)人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤舶沿,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后配并,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體括荡,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年溉旋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了畸冲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡观腊,死狀恐怖邑闲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梧油,我是刑警寧澤苫耸,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站儡陨,受9級(jí)特大地震影響褪子,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骗村,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一嫌褪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胚股,春花似錦渔扎、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至财忽,卻和暖如春倘核,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背即彪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工紧唱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人隶校。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓漏益,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親深胳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子绰疤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 本文主要簡(jiǎn)單介紹下IOS自帶的語(yǔ)音播報(bào)功能,有需要的可以參考下 一舞终,使用iOS自帶TTS需要注意的幾點(diǎn): 1.iO...
    Liebling_zn閱讀 2,366評(píng)論 0 1
  • iOS文本轉(zhuǎn)語(yǔ)音技術(shù) 文本轉(zhuǎn)語(yǔ)音技術(shù), 也叫TTS, 是Text To Speech的縮寫(xiě). iOS如果想做有聲書(shū)...
    windgo閱讀 4,414評(píng)論 7 10
  • AVSpeechSynthesizer 功能 將文字添加到語(yǔ)音, 就是用語(yǔ)音播放一段文字屬性 delegate :...
    yadong閱讀 591評(píng)論 0 0
  • iOS7到現(xiàn)在已經(jīng)發(fā)布了有一段時(shí)間了轻庆。相信你現(xiàn)在已經(jīng)了解了它那些開(kāi)創(chuàng)性的視覺(jué)設(shè)計(jì),已經(jīng)了解了它的新的API敛劝,比如說(shuō)...
    全力以赴打醬油閱讀 1,007評(píng)論 1 0
  • 前言 在IOS7之前一般語(yǔ)音識(shí)別是靠一些第三方庫(kù)余爆,iOS7之后,蘋(píng)果提供了文字轉(zhuǎn)語(yǔ)音的API可以使用夸盟。 導(dǎo)入框架 ...
    超_愛(ài)編程閱讀 5,704評(píng)論 5 2