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

Text-to-Speech-support-#53 #60

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will need to call updateNowPlayingInfo() here.

}
}

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