Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cspell-wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,5 @@ fishjam
Fishjam
deinitialize
Deinitialize
phonemize
phonemization
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
[submodule "third-party/googletest"]
path = third-party/googletest
url = https://github.com/google/googletest.git
[submodule "packages/react-native-executorch/third-party/common/phonemis"]
path = packages/react-native-executorch/third-party/common/phonemis
url = https://github.com/IgorSwat/Phonemis
branch = main
111 changes: 63 additions & 48 deletions apps/speech/components/ModelPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useEffect, useRef, useState } from 'react';
import {
Dimensions,
Modal,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
TouchableWithoutFeedback,
View,
} from 'react-native';

Expand All @@ -21,7 +23,7 @@
disabled?: boolean;
};

const DROPDOWN_MAX_HEIGHT = 200;
const DROPDOWN_MAX_HEIGHT = 300;

export function ModelPicker<T>({
models,
Expand All @@ -31,8 +33,11 @@
disabled,
}: Props<T>) {
const [open, setOpen] = useState(false);
const [triggerHeight, setTriggerHeight] = useState(0);
const [expandUp, setExpandUp] = useState(false);
const [dropdownLayout, setDropdownLayout] = useState({
x: 0,
y: 0,
width: 0,
});
const triggerRef = useRef<React.ComponentRef<typeof TouchableOpacity>>(null);
const selected = models.find((m) => m.value === selectedModel);

Expand All @@ -50,23 +55,22 @@
(
_x: number,
_y: number,
_width: number,
width: number,
height: number,
_pageX: number,
pageX: number,
pageY: number
) => {
setTriggerHeight(height);
const spaceBelow = Dimensions.get('window').height - (pageY + height);
setExpandUp(spaceBelow < DROPDOWN_MAX_HEIGHT);
const y =
spaceBelow >= DROPDOWN_MAX_HEIGHT
? pageY + height + 2
: pageY - Math.min(DROPDOWN_MAX_HEIGHT, models.length * 42) - 2;
setDropdownLayout({ x: pageX, y, width });
setOpen(true);
}
);
};

const dropdownPosition = expandUp
? { bottom: triggerHeight + 2 }
: { top: triggerHeight + 2 };

return (
<View style={styles.container}>
<TouchableOpacity
Expand All @@ -80,36 +84,52 @@
<Text style={styles.chevron}>{open ? 'โ–ฒ' : 'โ–ผ'}</Text>
</TouchableOpacity>

{open && (
<ScrollView
style={[styles.dropdown, dropdownPosition]}
nestedScrollEnabled
keyboardShouldPersistTaps="handled"
>
{models.map((item) => {
const isSelected = item.value === selectedModel;
return (
<TouchableOpacity
key={item.label}
style={[styles.option, isSelected && styles.optionSelected]}
onPress={() => {
onSelect(item.value);
setOpen(false);
}}
>
<Text
style={[
styles.optionText,
isSelected && styles.optionTextSelected,
]}
>
{item.label}
</Text>
</TouchableOpacity>
);
})}
</ScrollView>
)}
<Modal
visible={open}
transparent
animationType="none"
onRequestClose={() => setOpen(false)}
>
<TouchableWithoutFeedback onPress={() => setOpen(false)}>
<View style={StyleSheet.absoluteFill}>
<ScrollView
style={[
styles.dropdown,
{

Check warning on line 98 in apps/speech/components/ModelPicker.tsx

View workflow job for this annotation

GitHub Actions / lint

Inline style: { position: 'absolute' }
position: 'absolute',
top: dropdownLayout.y,
left: dropdownLayout.x,
width: dropdownLayout.width,
},
]}
keyboardShouldPersistTaps="handled"
>
{models.map((item) => {
const isSelected = item.value === selectedModel;
return (
<TouchableOpacity
key={item.label}
style={[styles.option, isSelected && styles.optionSelected]}
onPress={() => {
onSelect(item.value);
setOpen(false);
}}
>
<Text
style={[
styles.optionText,
isSelected && styles.optionTextSelected,
]}
>
{item.label}
</Text>
</TouchableOpacity>
);
})}
</ScrollView>
</View>
</TouchableWithoutFeedback>
</Modal>
</View>
);
}
Expand All @@ -119,7 +139,6 @@
marginHorizontal: 12,
marginVertical: 4,
alignSelf: 'stretch',
zIndex: 100,
},
trigger: {
flexDirection: 'row',
Expand Down Expand Up @@ -151,19 +170,15 @@
marginLeft: 6,
},
dropdown: {
position: 'absolute',
left: 0,
right: 0,
borderWidth: 1,
borderColor: '#C1C6E5',
borderRadius: 8,
backgroundColor: '#fff',
maxHeight: DROPDOWN_MAX_HEIGHT,
zIndex: 100,
elevation: 4,
elevation: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowOpacity: 0.15,
shadowRadius: 4,
},
option: {
Expand Down
2 changes: 1 addition & 1 deletion apps/speech/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"metro-config": "^0.83.0",
"react": "19.2.5",
"react-native": "0.83.4",
"react-native-audio-api": "0.12.0",
"react-native-audio-api": "0.11.5",
"react-native-device-info": "^15.0.2",
"react-native-executorch": "workspace:*",
"react-native-executorch-expo-resource-fetcher": "workspace:*",
Expand Down
8 changes: 2 additions & 6 deletions apps/speech/screens/Quiz.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import Animated, {
} from 'react-native-reanimated';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import {
KOKORO_MEDIUM,
KOKORO_VOICE_AM_SANTA,
KOKORO_AMERICAN_ENGLISH_MALE_SANTA,
useTextToSpeech,
} from 'react-native-executorch';
import {
Expand Down Expand Up @@ -60,10 +59,7 @@ const createAudioBufferFromVector = (

export const Quiz = ({ onBack }: { onBack: () => void }) => {
// --- Hooks & State ---
const model = useTextToSpeech({
model: KOKORO_MEDIUM,
voice: KOKORO_VOICE_AM_SANTA,
});
const model = useTextToSpeech(KOKORO_AMERICAN_ENGLISH_MALE_SANTA);

const [shuffledQuestions] = useState(() => shuffleArray(QUESTIONS));
const [currentIndex, setCurrentIndex] = useState(0);
Expand Down
8 changes: 2 additions & 6 deletions apps/speech/screens/TextToSpeechLLMScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import SWMIcon from '../assets/swm_icon.svg';
import {
useLLM,
useTextToSpeech,
KOKORO_MEDIUM,
KOKORO_VOICE_AF_HEART,
KOKORO_AMERICAN_ENGLISH_FEMALE_HEART,
LLAMA3_2_1B_QLORA,
} from 'react-native-executorch';
import {
Expand Down Expand Up @@ -54,10 +53,7 @@ export const TextToSpeechLLMScreen = ({ onBack }: TextToSpeechLLMProps) => {
const [displayText, setDisplayText] = useState('');
const [isTtsStreaming, setIsTtsStreaming] = useState(false);
const llm = useLLM({ model: LLAMA3_2_1B_QLORA });
const tts = useTextToSpeech({
model: KOKORO_MEDIUM,
voice: KOKORO_VOICE_AF_HEART,
});
const tts = useTextToSpeech(KOKORO_AMERICAN_ENGLISH_FEMALE_HEART);

const processedLengthRef = useRef(0);
const audioContextRef = useRef<AudioContext | null>(null);
Expand Down
Loading
Loading