Skip to content
This repository has been archived by the owner on Nov 26, 2020. It is now read-only.

Commit

Permalink
Text-to-Speech-support-#53
Browse files Browse the repository at this point in the history
  • Loading branch information
Hoang The Hung authored and Hoang The Hung committed Apr 3, 2016
1 parent 0eff38c commit e620546
Show file tree
Hide file tree
Showing 4 changed files with 409 additions and 21 deletions.
124 changes: 113 additions & 11 deletions Source/FolioReaderAudioPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ import UIKit
import AVFoundation
import MediaPlayer

class FolioReaderAudioPlayer: NSObject, AVAudioPlayerDelegate {
protocol FolioReaderAudioPlayerDelegate {
/**
Notifies that Player read all sentence
*/
func didReadSentence()
}

class FolioReaderAudioPlayer: NSObject, AVAudioPlayerDelegate, AVSpeechSynthesizerDelegate {
var delegate: FolioReaderAudioPlayerDelegate!
var isTextToSpeech = false
var synthesizer: AVSpeechSynthesizer!
var playing = false
var player: AVAudioPlayer!
var currentHref: String!
Expand All @@ -22,16 +31,21 @@ class FolioReaderAudioPlayer: NSObject, AVAudioPlayerDelegate {
var currentEndTime: Double!
var playingTimer: NSTimer!
var registeredCommands = false

var completionHandler: () -> Void = {}
var utteranceRate: float_t = 0
override init() {
super.init()
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()

// this is needed to the audio can play even when the "silent/vibrate" toggle is on
let session:AVAudioSession = AVAudioSession.sharedInstance()
try! session.setCategory(AVAudioSessionCategoryPlayback)
try! session.setActive(true)


}


deinit {
UIApplication.sharedApplication().endReceivingRemoteControlEvents()
}
Expand Down Expand Up @@ -61,23 +75,82 @@ class FolioReaderAudioPlayer: NSObject, AVAudioPlayerDelegate {

updateNowPlayingInfo()
}
if( synthesizer != nil){
// Need to change between version IOS
// http://stackoverflow.com/questions/32761786/ios9-avspeechutterance-rate-for-avspeechsynthesizer-issue
if #available(iOS 9, *) {
switch rate {
case 0:
utteranceRate = 0.42
break
case 1:
utteranceRate = 0.5
break
case 2:
utteranceRate = 0.53
break
case 3:
utteranceRate = 0.56
break
default:
break
}
} else {
switch rate {
case 0:
utteranceRate = 0
break
case 1:
utteranceRate = 0.06
break
case 2:
utteranceRate = 0.15
break
case 3:
utteranceRate = 0.23
break
default:
break
}
}

}
}

func stop() {
playing = false
if( player != nil && player.playing ){
player.stop()

UIApplication.sharedApplication().idleTimerDisabled = false
}
if (!isTextToSpeech) {
if (player != nil && player.playing) {
player.stop()

UIApplication.sharedApplication().idleTimerDisabled = false
}
} else {
synthesizer.stopSpeakingAtBoundary(AVSpeechBoundary.Word)
}
}

func stopSynthesizer(stopCompletion: ()->Void){
playing = false
synthesizer.stopSpeakingAtBoundary(AVSpeechBoundary.Word)
completionHandler = stopCompletion
}

func pause() {
playing = false
if( player != nil && player.playing ){
player.pause()

if(!isTextToSpeech){

UIApplication.sharedApplication().idleTimerDisabled = false
if( player != nil && player.playing ){
player.pause()

UIApplication.sharedApplication().idleTimerDisabled = false
}

}else{
if (synthesizer.speaking) {
synthesizer.pauseSpeakingAtBoundary(AVSpeechBoundary.Word)
}
}
}

Expand All @@ -86,6 +159,7 @@ class FolioReaderAudioPlayer: NSObject, AVAudioPlayerDelegate {
}

func playAudio() {
isTextToSpeech = false;
let currentPage = FolioReader.sharedInstance.readerCenter.currentPage
currentPage.playAudio()

Expand Down Expand Up @@ -249,7 +323,35 @@ class FolioReaderAudioPlayer: NSObject, AVAudioPlayerDelegate {

return nextAudioFragment()
}


func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didCancelSpeechUtterance utterance: AVSpeechUtterance) {
completionHandler()
}

func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance)
{
if(isPlaying()){
delegate.didReadSentence()
}
}

func playText(text: String) {
isTextToSpeech = true;
playing = true;
if((synthesizer) == nil){
synthesizer = AVSpeechSynthesizer()
synthesizer.delegate = self;
setRate(1);
}

let utterance = AVSpeechUtterance(string: text)
utterance.rate = utteranceRate
if(synthesizer.speaking){
synthesizer.stopSpeakingAtBoundary(AVSpeechBoundary.Word)
}
synthesizer.speakUtterance(utterance)
}

// MARK: - Audio timing events

private func startPlayerTimer() {
Expand Down
8 changes: 5 additions & 3 deletions Source/FolioReaderCenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,7 @@ class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UICollectio
rightBarIcons.append(UIBarButtonItem(image: shareIcon, style: UIBarButtonItemStyle.Plain, target: self, action:"shareChapter:"))
}

if book.hasAudio() {
rightBarIcons.append(UIBarButtonItem(image: audioIcon, style: UIBarButtonItemStyle.Plain, target: self, action:"togglePlay:"))
}
rightBarIcons.append(UIBarButtonItem(image: audioIcon, style: UIBarButtonItemStyle.Plain, target: self, action:"togglePlay:"))

navigationItem.rightBarButtonItems = rightBarIcons
}
Expand Down Expand Up @@ -741,6 +739,10 @@ class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UICollectio
if (completion != nil) { completion!() }
}
}

func isLastPage() -> Bool{
return currentPageNumber == nextPageNumber
}

func changePageToNext(completion: (() -> Void)? = nil) {
changePageWith(page: nextPageNumber, animated: true) { () -> Void in
Expand Down
63 changes: 56 additions & 7 deletions Source/FolioReaderPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import JSQWebViewController
optional func pageDidLoad(page: FolioReaderPage)
}

class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGestureRecognizerDelegate {
class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGestureRecognizerDelegate, FolioReaderAudioPlayerDelegate {

var pageNumber: Int!
var webView: UIWebView!
Expand Down Expand Up @@ -95,10 +95,22 @@ class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGestureRecogni
webView.loadHTMLString(html as String, baseURL: baseURL)
}

// MARK: - FolioReaderAudioPlayerDelegate
func didReadSentence() {
self.readCurrentSentence();
}

// MARK: - UIWebView Delegate

func webViewDidFinishLoad(webView: UIWebView) {

if (!book.hasAudio()) {
FolioReader.sharedInstance.readerAudioPlayer.delegate = self;
self.webView.js("wrappingSentencesWithinPTags()");
if (FolioReader.sharedInstance.readerAudioPlayer.isPlaying()) {
readCurrentSentence()
}
}

webView.scrollView.contentSize = CGSizeMake(pageWidth, webView.scrollView.contentSize.height)

if scrollDirection == .Down && isScrolling {
Expand All @@ -114,8 +126,10 @@ class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGestureRecogni
webView.isColors = false
self.webView.createMenu(options: false)
}

delegate.pageDidLoad!(self)


}

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
Expand Down Expand Up @@ -290,8 +304,44 @@ class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGestureRecogni
}

func playAudio(){
webView.play(nil)
if (book.hasAudio()) {
webView.js("playAudio()")
} else {
readCurrentSentence()
}
}

func speakSentence(){
let sentence = self.webView.js("getSentenceWithIndex('\(book.playbackActiveClass())')")
if ((sentence) != nil) {
FolioReader.sharedInstance.readerAudioPlayer.playText(sentence!)
}else{
if(FolioReader.sharedInstance.readerCenter.isLastPage()){
FolioReader.sharedInstance.readerAudioPlayer.stop()
}else{
FolioReader.sharedInstance.readerCenter.changePageToNext()
}
}
}

func readCurrentSentence() {
if (FolioReader.sharedInstance.readerAudioPlayer.synthesizer == nil ) {
speakSentence()
} else {
if(FolioReader.sharedInstance.readerAudioPlayer.synthesizer.paused){
FolioReader.sharedInstance.readerAudioPlayer.synthesizer.continueSpeaking()
}else{
if(FolioReader.sharedInstance.readerAudioPlayer.synthesizer.speaking){
FolioReader.sharedInstance.readerAudioPlayer.stopSynthesizer({ () -> Void in
self.webView.js("resetCurrentSentenceIndex()")
self.speakSentence()
})
}else{
speakSentence()
}
}
}
}

func audioMarkID(ID: String){
self.webView.js("audioMarkID('\(book.playbackActiveClass())','\(ID)')");
Expand Down Expand Up @@ -340,7 +390,7 @@ extension UIWebView {

if action == "highlight:"
|| (action == "define:" && (js("getSelectedText()"))!.componentsSeparatedByString(" ").count == 1)
|| (action == "play:" && book.hasAudio() )
|| (action == "play:")
|| (action == "share:" && readerConfig.allowSharing == true)
|| (action == "copy:" && readerConfig.allowSharing == true) {
return true
Expand Down Expand Up @@ -421,8 +471,7 @@ extension UIWebView {
}

func play(sender: UIMenuController?) {

js("playAudio()")
FolioReader.sharedInstance.readerCenter.currentPage.playAudio()

// Force remove text selection
// @NOTE: this doesn't seem to always work
Expand Down

0 comments on commit e620546

Please sign in to comment.