src/speech.js
/**
	 * Start playing current chunk and queue playing the next.
	 */
Start playing current chunk and queue playing the next.
Start playing current chunk and queue playing the next .
startPlayingCurrentChunkAndQueueNext() {
		clearTimeout( this.state.speakTimeoutId );
		const reject = ( reason ) => {
			if ( ! reason || 'playback_interrupted' === reason || 'playback_completed' === reason ) {
				this.setState( { playing: false } );
			}
		};
		const speakingStoppedPromise = new Promise( ( resolve ) => {
			if ( speechSynthesis.speaking ) {
				const CANCEL_WAIT_TIMEOUT = 100;
				if ( this.currentUtterance ) {
					this.currentUtterance.addEventListener( 'end', resolve );
				}
				setTimeout( resolve, CANCEL_WAIT_TIMEOUT );
				speechSynthesis.cancel();
			} else {
				resolve();
			}
		} );
		const queueNextChunk = () => {
			if ( this.state.chunkIndex + 1 === this.chunks.length ) {
				reject( 'playback_completed' );
				return;
			}
			const thisChunk = this.chunks[ this.state.chunkIndex ];
			const nextChunk = this.chunks[ this.state.chunkIndex + 1 ];
			const pauseDuration = this.getInterChunkPause( thisChunk, nextChunk );
			const currentChunk = this.state.chunkIndex;
			this.setState( {
				speakTimeoutId: setTimeout( () => {
					this.setState( {
						chunkIndex: currentChunk + 1, 
						chunkRangeOffset: 0, 
					} );
				}, Math.round( pauseDuration * ( 1 / this.state.rate ) ) ),
			} );
		};
		Promise.all( [ voices.load(), speakingStoppedPromise ] ).then( () => {
			this.setState( {
				speakTimeoutId: setTimeout( () => {
					this.speakChunk().then( queueNextChunk, reject );
				} ),
			} );
		}, reject );
	}
startPlayingCurrentChunkAndQueueNext ( ) { clearTimeout ( this . state . speakTimeoutId ) ; const reject = ( reason ) => { if ( ! reason || 'playback_interrupted' === reason || 'playback_completed' === reason ) { this . setState ( { playing : false } ) ; } } ; const speakingStoppedPromise = new Promise ( ( resolve ) => { if ( speechSynthesis . speaking ) { const CANCEL_WAIT_TIMEOUT = 100 ; if ( this . currentUtterance ) { this . currentUtterance . addEventListener ( 'end' , resolve ) ; } setTimeout ( resolve , CANCEL_WAIT_TIMEOUT ) ; speechSynthesis . cancel ( ) ; } else { resolve ( ) ; } } ) ; const queueNextChunk = ( ) => { if ( this . state . chunkIndex + 1 === this . chunks . length ) { reject ( 'playback_completed' ) ; return ; } const thisChunk = this . chunks [ this . state . chunkIndex ] ; const nextChunk = this . chunks [ this . state . chunkIndex + 1 ] ; const pauseDuration = this . getInterChunkPause ( thisChunk , nextChunk ) ; const currentChunk = this . state . chunkIndex ; this . setState ( { speakTimeoutId : setTimeout ( ( ) => { this . setState ( { chunkIndex : currentChunk + 1 , chunkRangeOffset : 0 , } ) ; } , Math . round ( pauseDuration * ( 1 / this . state . rate ) ) ) , } ) ; } ; Promise . all ( [ voices . load ( ) , speakingStoppedPromise ] ) . then ( ( ) => { this . setState ( { speakTimeoutId : setTimeout ( ( ) => { this . speakChunk ( ) . then ( queueNextChunk , reject ) ; } ) , } ) ; } , reject ) ; }
Start playing current chunk and queue playing the next.
Start playing current chunk and queue playing the next .
// Wait for cancelled speaking to be confirmed to be ended. Needed in Chrome. // This state change will cause startPlayingCurrentChunkAndQueueNext to be called. // Start at beginning of chunk. // Make sure voices are loaded and speech synthesis has been completely stopped (since cancel is apparently async).