Skip to content

Commit d4e032e

Browse files
committed
Update example
1 parent 65db077 commit d4e032e

3 files changed

Lines changed: 152 additions & 6 deletions

File tree

‎example/package.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"@react-native-documents/picker": "^11.0.3",
1414
"react": "19.2.0",
1515
"react-native": "0.83.0",
16-
"react-native-nitro-modules": "^0.31.10"
16+
"react-native-nitro-modules": "^0.31.10",
17+
"react-native-video": "^6.0.0-beta.6"
1718
},
1819
"devDependencies": {
1920
"@babel/core": "^7.25.2",

‎example/src/App.tsx‎

Lines changed: 128 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from 'react';
1+
import { useState, useRef, useEffect } from 'react';
22
import {
33
Text,
44
View,
@@ -11,14 +11,15 @@ import {
1111
} from 'react-native';
1212

1313
import { pick, types } from '@react-native-documents/picker';
14+
import Video from 'react-native-video';
1415
import {
1516
getRawPcmData,
1617
getWaveformDataByPoints,
1718
getWaveformData,
1819
type WaveformMethod,
1920
} from 'react-native-audio-data';
2021

21-
const WaveformView = ({ data }: { data: number[] }) => {
22+
const WaveformView = ({ data, progress }: { data: number[], progress: number }) => {
2223
if (!data || data.length === 0) {
2324
return (
2425
<View style={[styles.waveformContainer, styles.emptyContainer]}>
@@ -45,14 +46,20 @@ const WaveformView = ({ data }: { data: number[] }) => {
4546
{data.map((value, index) => {
4647
let heightPercent = value * scale * 100;
4748
heightPercent = Math.max(heightPercent, 2);
49+
50+
// Determine if this bar is "played"
51+
// progress is 0..1. The index corresponding to progress is floor(progress * data.length)
52+
const isPlayed = index / data.length < progress;
53+
4854
return (
4955
<View
5056
key={index}
5157
style={[
5258
styles.bar,
5359
{
5460
height: `${heightPercent}%`,
55-
opacity: 0.5 + value * scale * 0.5,
61+
opacity: isPlayed ? 1.0 : (0.5 + value * scale * 0.5),
62+
backgroundColor: isPlayed ? '#00ff00' : '#00e5ff',
5663
},
5764
]}
5865
/>
@@ -97,15 +104,75 @@ export default function App() {
97104
const [method, setMethod] = useState<WaveformMethod>('RMS');
98105
const [mode, setMode] = useState<'points' | 'ms'>('points');
99106

107+
// Video Player State
108+
const [paused, setPaused] = useState(true);
109+
const [progress, setProgress] = useState(0);
110+
const [duration, setDuration] = useState(0);
111+
112+
// Animation Refs
113+
const lastUpdate = useRef<{ time: number; timestamp: number } | null>(null);
114+
const animationFrame = useRef<number | null>(null);
115+
100116
const methods: WaveformMethod[] = ['RMS', 'LUFS', 'AbsMean'];
101117

118+
const startAnimationLoop = () => {
119+
if (animationFrame.current) cancelAnimationFrame(animationFrame.current);
120+
121+
const loop = () => {
122+
if (lastUpdate.current && duration > 0) {
123+
const now = Date.now();
124+
const elapsed = (now - lastUpdate.current.timestamp) / 1000;
125+
const estimatedTime = lastUpdate.current.time + elapsed;
126+
const newProgress = Math.min(estimatedTime / duration, 1.0);
127+
setProgress(newProgress);
128+
}
129+
animationFrame.current = requestAnimationFrame(loop);
130+
};
131+
animationFrame.current = requestAnimationFrame(loop);
132+
};
133+
134+
const stopAnimationLoop = () => {
135+
if (animationFrame.current) {
136+
cancelAnimationFrame(animationFrame.current);
137+
animationFrame.current = null;
138+
}
139+
};
140+
141+
// Cleanup on unmount
142+
useEffect(() => {
143+
return () => stopAnimationLoop();
144+
}, []);
145+
146+
const togglePlayback = () => {
147+
const nextPaused = !paused;
148+
setPaused((prev) => !prev);
149+
150+
if (nextPaused) {
151+
stopAnimationLoop();
152+
} else {
153+
// Reset last update if starting from scratch or re-playing
154+
if (progress >= 1) {
155+
lastUpdate.current = { time: 0, timestamp: Date.now() };
156+
setProgress(0);
157+
}
158+
startAnimationLoop();
159+
}
160+
};
161+
102162
const handlePickAndProcess = async () => {
103163
try {
104164
setLoading(true);
105165
setLog('Picking file...');
106166
setSelectedPath(null);
107167
setWaveformData([]);
108168

169+
// Reset player state
170+
setPaused(true);
171+
setProgress(0);
172+
setDuration(0);
173+
stopAnimationLoop();
174+
lastUpdate.current = null;
175+
109176
const results = await pick({
110177
type: [types.audio],
111178
allowMultiSelection: false,
@@ -186,7 +253,47 @@ export default function App() {
186253
</View>
187254
)}
188255

189-
<WaveformView data={waveformData} />
256+
<WaveformView data={waveformData} progress={progress} />
257+
258+
{selectedPath && (
259+
<View style={styles.playerContainer}>
260+
<Video
261+
source={{ uri: selectedPath }}
262+
paused={paused}
263+
onLoad={(data) => {
264+
setDuration(data.duration);
265+
console.log('Video loaded', data.duration);
266+
}}
267+
onProgress={(data) => {
268+
// Sync authoritative time
269+
lastUpdate.current = { time: data.currentTime, timestamp: Date.now() };
270+
271+
// If paused, we want exact sync. If playing, the loop handles it.
272+
if (paused && duration > 0) {
273+
setProgress(data.currentTime / duration);
274+
}
275+
}}
276+
onEnd={() => {
277+
setPaused(true);
278+
setProgress(1);
279+
stopAnimationLoop();
280+
console.log('Video ended');
281+
}}
282+
onError={(e) => console.log('Video error', e)}
283+
audioOnly={true}
284+
repeat={false}
285+
ignoreSilentSwitch="ignore"
286+
progressUpdateInterval={250}
287+
/>
288+
<Button
289+
title={paused ? "Play Audio" : "Pause"}
290+
onPress={togglePlayback}
291+
/>
292+
<Text style={styles.progressText}>
293+
{(progress * duration).toFixed(1)}s / {duration.toFixed(1)}s
294+
</Text>
295+
</View>
296+
)}
190297

191298
<View style={styles.settingsContainer}>
192299
<View style={styles.settingRow}>
@@ -260,7 +367,7 @@ const styles = StyleSheet.create({
260367
alignItems: 'stretch',
261368
},
262369
header: {
263-
fontSize: 24,
370+
fontSize: 20,
264371
fontWeight: 'bold',
265372
textAlign: 'center',
266373
marginBottom: 20,
@@ -427,4 +534,20 @@ const styles = StyleSheet.create({
427534
fontFamily: 'monospace',
428535
fontSize: 12,
429536
},
537+
playerContainer: {
538+
backgroundColor: '#fff',
539+
borderRadius: 8,
540+
padding: 10,
541+
marginBottom: 15,
542+
flexDirection: 'row',
543+
alignItems: 'center',
544+
justifyContent: 'space-between',
545+
borderWidth: 1,
546+
borderColor: '#ddd',
547+
},
548+
progressText: {
549+
fontSize: 14,
550+
color: '#555',
551+
fontWeight: 'bold',
552+
},
430553
});

‎yarn.lock‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,6 +2116,16 @@ __metadata:
21162116
languageName: node
21172117
linkType: hard
21182118

2119+
"@react-native-documents/picker@npm:^11.0.3":
2120+
version: 11.0.4
2121+
resolution: "@react-native-documents/picker@npm:11.0.4"
2122+
peerDependencies:
2123+
react: "*"
2124+
react-native: ">=0.79.0"
2125+
checksum: 10c0/041bb024f623ebc7db10b9a9e4dad7fd76b00d1a3097e13bf04b89bf6e30947ef4c1fdb653d3adcaae0bc40e94fc06aec7317a66a3fd670cb521d81dc3c19acf
2126+
languageName: node
2127+
linkType: hard
2128+
21192129
"@react-native/assets-registry@npm:0.83.0":
21202130
version: 0.83.0
21212131
resolution: "@react-native/assets-registry@npm:0.83.0"
@@ -7390,6 +7400,7 @@ __metadata:
73907400
"@react-native-community/cli": "npm:20.0.0"
73917401
"@react-native-community/cli-platform-android": "npm:20.0.0"
73927402
"@react-native-community/cli-platform-ios": "npm:20.0.0"
7403+
"@react-native-documents/picker": "npm:^11.0.3"
73937404
"@react-native/babel-preset": "npm:0.83.0"
73947405
"@react-native/metro-config": "npm:0.83.0"
73957406
"@react-native/typescript-config": "npm:0.83.0"
@@ -7399,6 +7410,7 @@ __metadata:
73997410
react-native-builder-bob: "npm:^0.40.13"
74007411
react-native-monorepo-config: "npm:^0.3.1"
74017412
react-native-nitro-modules: "npm:^0.31.10"
7413+
react-native-video: "npm:^6.0.0-beta.6"
74027414
languageName: unknown
74037415
linkType: soft
74047416

@@ -7483,6 +7495,16 @@ __metadata:
74837495
languageName: node
74847496
linkType: hard
74857497

7498+
"react-native-video@npm:^6.0.0-beta.6":
7499+
version: 6.18.0
7500+
resolution: "react-native-video@npm:6.18.0"
7501+
peerDependencies:
7502+
react: "*"
7503+
react-native: "*"
7504+
checksum: 10c0/4810181cb99753a78db2081da0b3d5a73c5eab60cd59f72cac7b7eb026378c353cea749ee8ab9ebcd5b12a0ad01818155b7cbba1c2674818034a353ab581a03c
7505+
languageName: node
7506+
linkType: hard
7507+
74867508
"react-native@npm:0.83.0":
74877509
version: 0.83.0
74887510
resolution: "react-native@npm:0.83.0"

0 commit comments

Comments
 (0)