Skip to content

Commit 637dee2

Browse files
committed
chore: update VolumeMeteringAvatar and docs
1 parent 6b93827 commit 637dee2

8 files changed

+490
-217
lines changed

README.md

+40-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ expo-speech-recognition implements the iOS [`SFSpeechRecognizer`](https://develo
1919
- [Transcribing audio files](#transcribing-audio-files)
2020
- [Supported input audio formats](#supported-input-audio-formats)
2121
- [File transcription example](#file-transcription-example)
22+
- [Volume metering](#volume-metering)
23+
- [Volume metering example](#volume-metering-example)
2224
- [Polyfilling the Web SpeechRecognition API](#polyfilling-the-web-speechrecognition-api)
2325
- [Muting the beep sound on Android](#muting-the-beep-sound-on-android)
2426
- [Improving accuracy of single-word prompts](#improving-accuracy-of-single-word-prompts)
@@ -538,6 +540,43 @@ function TranscribeAudioFile() {
538540
}
539541
```
540542

543+
## Volume metering
544+
545+
You can use the `volumeChangeEventOptions.enabled` option to enable volume metering. This will emit a `volumechange` event with the current volume level (between -2 and 10) as a value. You can use this value to animate the volume metering of a user's voice, or to provide feedback to the user about the volume level.
546+
547+
### Volume metering example
548+
549+
![Volume metering example](./images/volume-metering.gif)
550+
551+
See: (link) for a complete example that involves using `react-native-reanimated` to animate the volume metering.
552+
553+
````tsx
554+
import { ExpoSpeechRecognitionModule } from "expo-speech-recognition";
555+
556+
function VolumeMeteringAvatar() {
557+
558+
useSpeechRecognitionEvent("volumechange", (event) => {
559+
console.log("Volume changed to:", event.value);
560+
});
561+
562+
const handleStart = () => {
563+
ExpoSpeechRecognitionModule.start({
564+
lang: "en-US",
565+
volumeChangeEventOptions: {
566+
enabled: true,
567+
intervalMillis: 300,
568+
},
569+
});
570+
};
571+
572+
return (
573+
<View>
574+
<Button title="Start" onPress={handleStart} />
575+
<Text>Volume: {volume}</Text>
576+
</View>
577+
);
578+
}
579+
541580
## Polyfilling the Web SpeechRecognition API
542581

543582
> [!IMPORTANT]
@@ -616,7 +655,7 @@ recognition.stop();
616655

617656
// Immediately cancel speech recognition
618657
recognition.abort();
619-
```
658+
````
620659

621660
## Muting the beep sound on Android
622661

example/App.tsx

+7-2
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,15 @@ export default function App() {
7272
continuous: true,
7373
requiresOnDeviceRecognition: false,
7474
addsPunctuation: true,
75-
contextualStrings: ["Carlsen", "Ian Nepomniachtchi", "Praggnanandhaa"],
75+
contextualStrings: [
76+
"expo-speech-recognition",
77+
"Carlsen",
78+
"Ian Nepomniachtchi",
79+
"Praggnanandhaa",
80+
],
7681
volumeChangeEventOptions: {
7782
enabled: false,
78-
intervalMillis: 300,
83+
intervalMillis: 100,
7984
},
8085
});
8186

example/babel.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const path = require("path");
2-
module.exports = function (api) {
2+
module.exports = (api) => {
33
api.cache(true);
44
return {
55
presets: ["babel-preset-expo"],

example/components/VolumeMeteringAvatar.tsx

+27-35
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,24 @@ import Animated, {
88
withTiming,
99
Easing,
1010
withSpring,
11-
withDelay,
1211
withSequence,
1312
} from "react-native-reanimated";
1413
const avatar = require("../assets/avatar.png");
1514

16-
const minScale = 1;
17-
const maxScale = 1.5;
15+
const MIN_SCALE = 1;
16+
const MAX_SCALE = 1.5;
1817

1918
/**
2019
* This is an example component that uses the `volumechange` event to animate the volume metering of a user's voice.
2120
*/
2221
export function VolumeMeteringAvatar() {
23-
const haloScale = useSharedValue(minScale);
24-
const pulseScale = useSharedValue(minScale);
22+
const haloScale = useSharedValue(MIN_SCALE);
23+
const pulseScale = useSharedValue(MIN_SCALE);
2524
const pulseOpacity = useSharedValue(0);
2625

27-
const haloAnimatedStyle = useAnimatedStyle(() => ({
28-
transform: [{ scale: haloScale.value }],
29-
}));
30-
31-
const pulseAnimatedStyle = useAnimatedStyle(() => ({
32-
opacity: pulseOpacity.value,
33-
transform: [{ scale: pulseScale.value }],
34-
}));
35-
3626
const reset = () => {
37-
haloScale.value = minScale;
38-
pulseScale.value = minScale;
27+
haloScale.value = MIN_SCALE;
28+
pulseScale.value = MIN_SCALE;
3929
pulseOpacity.value = 0;
4030
};
4131

@@ -51,7 +41,7 @@ export function VolumeMeteringAvatar() {
5141
const newScale = interpolate(
5242
event.value,
5343
[-2, 10], // The value range is between -2 and 10
54-
[minScale, maxScale],
44+
[MIN_SCALE, MAX_SCALE],
5545
Extrapolation.CLAMP,
5646
);
5747

@@ -61,38 +51,40 @@ export function VolumeMeteringAvatar() {
6151
damping: 15,
6252
stiffness: 150,
6353
}),
64-
withTiming(minScale, {
54+
withTiming(MIN_SCALE, {
6555
duration: 500,
6656
easing: Easing.linear,
6757
}),
6858
);
6959

7060
// Animate the pulse (scale and fade out)
71-
if (pulseScale.value < newScale) {
72-
pulseScale.value = withSequence(
73-
withTiming(maxScale, {
74-
duration: 1000,
75-
easing: Easing.out(Easing.quad),
76-
}),
77-
withTiming(minScale, {
78-
duration: 300,
79-
easing: Easing.linear,
80-
}),
81-
);
82-
pulseOpacity.value = withSequence(
83-
withTiming(1, { duration: 800 }),
84-
withDelay(300, withTiming(0, { duration: 200 })),
85-
);
61+
if (pulseOpacity.value <= 0) {
62+
pulseScale.value = MIN_SCALE;
63+
pulseOpacity.value = 1;
64+
pulseScale.value = withTiming(MAX_SCALE, {
65+
duration: 1000,
66+
easing: Easing.out(Easing.quad),
67+
});
68+
pulseOpacity.value = withTiming(0, { duration: 1000 });
8669
}
8770
});
8871

72+
const haloAnimatedStyle = useAnimatedStyle(() => ({
73+
transform: [{ scale: haloScale.value }],
74+
}));
75+
76+
const pulseAnimatedStyle = useAnimatedStyle(() => ({
77+
opacity: pulseOpacity.value,
78+
transform: [{ scale: pulseScale.value }],
79+
}));
80+
8981
return (
9082
<View style={styles.container}>
9183
<View style={styles.pulseContainer}>
92-
<Animated.View style={[styles.pulse, pulseAnimatedStyle]} />
84+
<Animated.View style={[styles.halo, haloAnimatedStyle]} />
9385
</View>
9486
<View style={styles.pulseContainer}>
95-
<Animated.View style={[styles.halo, haloAnimatedStyle]} />
87+
<Animated.View style={[styles.pulse, pulseAnimatedStyle]} />
9688
</View>
9789
<View style={[styles.centered]}>
9890
<Image source={avatar} style={styles.avatar} />

example/ios/Podfile.lock

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ PODS:
66
- ReactCommon/turbomodule/core
77
- EXConstants (16.0.2):
88
- ExpoModulesCore
9-
- Expo (51.0.36):
9+
- Expo (51.0.37):
1010
- ExpoModulesCore
1111
- ExpoAsset (10.0.10):
1212
- ExpoModulesCore
@@ -39,9 +39,9 @@ PODS:
3939
- ReactCommon/turbomodule/bridging
4040
- ReactCommon/turbomodule/core
4141
- Yoga
42-
- ExpoSpeechRecognition (0.2.22):
42+
- ExpoSpeechRecognition (0.2.23):
4343
- ExpoModulesCore
44-
- EXSplashScreen (0.27.5):
44+
- EXSplashScreen (0.27.6):
4545
- DoubleConversion
4646
- ExpoModulesCore
4747
- glog
@@ -1461,14 +1461,14 @@ SPEC CHECKSUMS:
14611461
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
14621462
EXAV: afa491e598334bbbb92a92a2f4dd33d7149ad37f
14631463
EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59
1464-
Expo: 51c2d7fee7a8c7195ac257c9f9503378800d334e
1464+
Expo: 67b60b3b80a3c8e9f3bcaaa84d06d140229a246d
14651465
ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875
14661466
ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51
14671467
ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238
14681468
ExpoKeepAwake: 3b8815d9dd1d419ee474df004021c69fdd316d08
14691469
ExpoModulesCore: 260ee156852434da26e782bbb993093186c5aade
1470-
ExpoSpeechRecognition: 6ce34ee84af8950f0e472ba6f6b0b3def2553b2a
1471-
EXSplashScreen: a7e8d13c476f9937e39d654af4235758b567a1be
1470+
ExpoSpeechRecognition: 078201eb1fbae87409cfecba6176edc262f75121
1471+
EXSplashScreen: 10b116117c9bb6a272ba782706f21dadc23f44d9
14721472
FBLazyVector: ac12dc084d1c8ec4cc4d7b3cf1b0ebda6dab85af
14731473
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
14741474
glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f

0 commit comments

Comments
 (0)