import React, {useState, useRef} from 'react';
import { View, StyleSheet, Text, TouchableOpacity, Dimensions, ScrollView } from 'react-native';
import * as Speech from 'expo-speech';
const windowWidth = Dimensions.get('window').width;
const sliderLeft = 36+16+50;
const sliderWidth = windowWidth - 2*sliderLeft+50;
const initRateValue = 2;
const initPitchValue = 1.0;
export default function App() {
const [voiceList, setVoiceList] = useState([]);
const [voiceId, setVoiceId] = useState("");
const rate = useRef(initRateValue);
const pitch = useRef(initPitchValue);
Speech.getAvailableVoicesAsync().then(r => {
setVoiceList(([r.filter(i => i.language == "en-US"), r.filter(i => i.language == "en-GB")]) || []);
setVoiceId(r.filter(i => i.language == "en-US")[0].identifier);
})
const speak = () => {
const thingToSay = '1';
Speech.speak(thingToSay);
};
const selectVoice = (identifier) => {
setVoiceId(identifier);
}
const settingAction = () => {
}
return (
<View style={styles.container}>
<Slider style={{marginHorizontal: 16, marginTop: 24}}
name="速度:"
value={initRateValue}
valueChanged={(i) => {rate.current = i}}/>
<Slider style={{marginHorizontal: 16, marginTop: 20}}
name="音调:"
value={initPitchValue}
valueChanged={(i) => {pitch.current = i} }/>
<ScrollView style={{flex: 1, backgroundColor: 'red', marginTop: 10}}>
{voiceList.map((list, i) =>
<View style={{backgroundColor: '#f5f6f7'}}>
<Text style={{lineHeight: 40, marginLeft: 16}} key={i}>{i == 0 ? "美音": "英音"}</Text>
{list.map((voice, index) =>
<TouchableOpacity onPress={() => selectVoice(voice.identifier)} activeOpacity={0.6} style={{backgroundColor: '#fff'}} key={`${i}_${index}`}>
<View style={{ flexDirection: 'row', alignItems: 'center', marginHorizontal: 16}}>
<Text style={{lineHeight: 54, fontSize: 16}}>{voice.name}</Text>
<View style={{flex: 1}}/>
<Text style={{fontSize: 14, color: '#67676a'}}>{voice.quality == "Default" ? "标准音质": "高级音质"}</Text>
<View style={{width: 10, height: 10, borderRadius: 5, backgroundColor: voiceId === voice.identifier ? 'red': '#eee', marginLeft: 15 }}/>
</View>
<View style={{height: 1, backgroundColor: '#ececef'}}/>
</TouchableOpacity>)}
</View>
)}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ffffff',
},
});
const Slider = ({style, name, value=1.1, valueChanged}) => {
const [_value, setValue] = useState(value);
const onPress = (event) => {
var v = ((event.nativeEvent.pageX - sliderLeft) / sliderWidth * 1.5 + 0.5).toFixed(1);
v = Math.max(0.5, Math.min(v, 2.0));
setValue(v);
valueChanged(0.8);
}
return <View style={style}>
<View style={{height: 40, flexDirection: 'row', alignItems: 'center'}}>
<Text style={{lineHeight: 40, width: 50}}>{name}</Text>
<Text style={{lineHeight: 40, width: 31}}>0.5</Text>
<TouchableOpacity onPress={(e) =>onPress(e)} activeOpacity={1} style={{flex: 1, height: 40, flexDirection: 'row', paddingHorizontal: 5}}>
<View style={{position: "absolute", left:5, right: 15, top: 19.5, height: 1, backgroundColor: '#e1e2e4'}}/>
<View style={{flex:(_value-0.5)/(1.5)}}/>
<View style={{ paddingVertical: 5, width: 30, height: 40, marginLeft: -15}}>
<View style={{ backgroundColor: 'white', alignItems: 'center', width: 30, height: 30,
shadowColor: "#000", shadowOpacity: 0.13, shadowRadius: 8, borderRadius: 15, elevation: 5}}>
<Text style={{color: "#6a6a67", fontSize: 11, fontWeight: 700, lineHeight: 30}}>{_value}</Text>
</View>
</View>
</TouchableOpacity>
<Text style={{lineHeight: 40, width: 21, textAlign: 'end'}}>2.0</Text>
</View>
</View>
}
class AudioArticlePlay {
const addListener = ({ onNext, onSpeakingChanged, didFinished }) => {
this._didFinished = didFinished;
this._onNext = onNext;
this._onSpeakingChanged = onSpeakingChanged;
}
const removeListener = () => {
this._didFinished = null;
this._onNext = null;
this._onSpeakingChanged = null;
}
set isSpeaking(value) {
this._isSpeaking = value;
if (this._onSpeakingChanged) {
this._onSpeakingChanged(value);
}
}
get isSpeaking() {
return this._isSpeaking;
}
get location() {
return this._location;
}
const releasDatas = () => {
this.removeListener();
this._article = null;
this._title = null;
this._id = null;
this._sentences = null;
this._location = null;
this._isSpeaking = false;
}
const setNewDatas = ({article, title, id}) => {
this._article = article;
this._title = title;
this._id = id;
this._sentences = getSentences(article);
this._location = {section: -1, item: 0};
}
const playSentence = () => {
const location = this._location;
var sentence = "";
if (location.section == -1) {
sentence = this._title;
} else {
sentence = this._sentences[location.section][location.item];
}
Speech.speak(sentence, {
voice: this._voice.identifier,
language: this._voice.language || "en_US",
rate: (this._voice.rate > 0.1) ? this._voice.rate: 1.0,
pitch: (this._voice.pitch> 0.1) ? this._voice.pitch: 1.0,
onDone: () => {
let next = this.getNextLocation();
if (next != null) {
if (this._id) {
this._location = next;
this.playSentence();
} else {
this.isSpeaking = false;
}
if (this._onNext) {
this._onNext(next);
}
} else {
if (this._didFinished) {
this._didFinished();
}
this.releasDatas();
}
},
onError: () => {
this.isSpeaking = false;
},
})
}
const newPlaySentence = async (l) => {
try {
const location = l || this._location;
this._location = location;
this._voice = JSON.parse(await AsyncStorage.getItem('Speech_Setting'));
await Speech.stop();
if (this._onNext) {
this._onNext(location);
}
this.playSentence();
this.isSpeaking = true;
} catch (e) {
this.isSpeaking = false;
}
}
const stop = async () => {
await Speech.stop();
this.isSpeaking = false;
}
const getNextLocation = () => {
return null;
}
}