@@ -6,17 +6,15 @@ import {
6
6
AudioBufferSourceNode ,
7
7
AudioManager ,
8
8
} from 'react-native-audio-api' ;
9
+ import { AudioPlayer } from '../../utils/AudioPlayer' ;
9
10
10
11
import { Container , Button , Spacer , Slider } from '../../components' ;
11
12
12
13
const URL =
13
14
'https://software-mansion.github.io/react-native-audio-api/audio/voice/example-voice-01.mp3' ;
14
15
15
- const LOOP_START = 0 ;
16
- const LOOP_END = 10 ;
17
-
18
16
const INITIAL_RATE = 1 ;
19
- const INITIAL_DETUNE = 0 ;
17
+ const TRACK_LENGTH = 25 ;
20
18
21
19
const labelWidth = 80 ;
22
20
@@ -26,72 +24,71 @@ const AudioFile: FC = () => {
26
24
27
25
const [ offset , setOffset ] = useState ( 0 ) ;
28
26
const [ playbackRate , setPlaybackRate ] = useState ( INITIAL_RATE ) ;
29
- const [ detune , setDetune ] = useState ( INITIAL_DETUNE ) ;
27
+ const [ elapsedTime , setElapsedTime ] = useState ( 0 ) ;
30
28
31
29
const [ audioBuffer , setAudioBuffer ] = useState < AudioBuffer | null > ( null ) ;
32
-
33
- const audioContextRef = useRef < AudioContext | null > ( null ) ;
34
- const bufferSourceRef = useRef < AudioBufferSourceNode | null > ( null ) ;
30
+ const timerRef = useRef < NodeJS . Timeout | null > ( null ) ;
35
31
36
32
const handlePlaybackRateChange = ( newValue : number ) => {
37
33
setPlaybackRate ( newValue ) ;
38
-
39
- if ( bufferSourceRef . current ) {
40
- bufferSourceRef . current . playbackRate . value = newValue ;
41
- }
34
+ audioPlayer . setPlaybackRate ( newValue ) ;
42
35
} ;
43
36
44
- const handleDetuneChange = ( newValue : number ) => {
45
- setDetune ( newValue ) ;
37
+ const audioContext = useMemo ( ( ) => new AudioContext ( ) , [ ] ) ;
38
+ const audioPlayer = useMemo ( ( ) => new AudioPlayer ( audioContext ) , [ ] ) ;
46
39
47
- if ( bufferSourceRef . current ) {
48
- bufferSourceRef . current . detune . value = newValue ;
40
+ useEffect ( ( ) => {
41
+ return ( ) => {
42
+ audioPlayer . stop ( ) ;
43
+ audioContext . close ( ) ;
49
44
}
50
- } ;
45
+ } , [ ] ) ;
46
+
47
+ useEffect ( ( ) => {
48
+ timerRef . current = setTimeout ( ( ) => {
49
+ if ( isPlaying ) {
50
+ setElapsedTime ( ( prev ) => prev + 1 ) ;
51
+ }
52
+ } , 1000 / playbackRate ) ;
53
+
54
+ return ( ) => clearTimeout ( timerRef . current ! ) ;
55
+ } , [ isPlaying , playbackRate , elapsedTime ] ) ;
56
+
57
+ useEffect ( ( ) => {
58
+ AudioManager . setLockScreenInfo ( {
59
+ elapsedTime : elapsedTime * 1000 ,
60
+ } ) ;
61
+ if ( elapsedTime > TRACK_LENGTH ) {
62
+ setElapsedTime ( 0 ) ;
63
+ setIsPlaying ( false ) ;
64
+ audioPlayer . stop ( ) ;
65
+ AudioManager . setLockScreenInfo ( {
66
+ state : 'state_paused' ,
67
+ } ) ;
68
+ }
69
+ } , [ elapsedTime ] ) ;
51
70
52
- const handlePress = ( ) => {
53
- if ( ! audioContextRef . current ) {
54
- return ;
55
- }
56
71
72
+ const handlePress = useCallback ( ( ) => {
57
73
if ( isPlaying ) {
58
- bufferSourceRef . current ?. stop ( audioContextRef . current . currentTime ) ;
74
+ audioPlayer . stop ( ) ;
59
75
AudioManager . setLockScreenInfo ( {
60
76
state : 'state_paused' ,
61
77
} ) ;
62
78
} else {
63
79
if ( ! audioBuffer ) {
64
80
fetchAudioBuffer ( ) ;
65
81
}
66
-
67
82
AudioManager . setLockScreenInfo ( {
68
83
state : 'state_playing' ,
69
- } ) ;
70
-
71
- AudioManager . observeAudioInterruptions ( true ) ;
72
-
73
- bufferSourceRef . current = audioContextRef . current . createBufferSource ( {
74
- pitchCorrection : true ,
75
- } ) ;
76
- bufferSourceRef . current . buffer = audioBuffer ;
77
- bufferSourceRef . current . loop = true ;
78
- bufferSourceRef . current . onended = ( event ) => {
79
- setOffset ( ( _prev ) => event . value || 0 ) ;
80
- } ;
81
- bufferSourceRef . current . loopStart = LOOP_START ;
82
- bufferSourceRef . current . loopEnd = LOOP_END ;
83
- bufferSourceRef . current . playbackRate . value = playbackRate ;
84
- bufferSourceRef . current . detune . value = detune ;
85
- bufferSourceRef . current . connect ( audioContextRef . current . destination ) ;
86
-
87
- bufferSourceRef . current . start (
88
- audioContextRef . current . currentTime ,
89
- offset
90
- ) ;
84
+ } )
85
+ audioPlayer . buffer = audioBuffer ! ;
86
+ audioPlayer . playbackRate = playbackRate ;
87
+ audioPlayer . play ( ) ;
91
88
}
92
89
93
90
setIsPlaying ( ( prev ) => ! prev ) ;
94
- } ;
91
+ } , [ isPlaying , audioBuffer , playbackRate ] ) ;
95
92
96
93
const fetchAudioBuffer = useCallback ( async ( ) => {
97
94
setIsLoading ( true ) ;
@@ -120,7 +117,7 @@ const AudioFile: FC = () => {
120
117
title : 'Audio file' ,
121
118
artist : 'Software Mansion' ,
122
119
album : 'Audio API' ,
123
- duration : 10 ,
120
+ duration : TRACK_LENGTH ,
124
121
} ) ;
125
122
126
123
const remotePlaySubscription = AudioManager . enableSystemEvent (
@@ -141,7 +138,15 @@ const AudioFile: FC = () => {
141
138
AudioManager . enableSystemEvent (
142
139
'remoteChangePlaybackPosition' ,
143
140
( event ) => {
144
- console . log ( 'remoteChangePlaybackPosition event:' , event ) ;
141
+ audioPlayer . stop ( ) ;
142
+ if ( isPlaying ) {
143
+ audioPlayer . play (
144
+ event . value || 0
145
+ ) ;
146
+ } else {
147
+ audioPlayer . offset = event . value || 0 ;
148
+ }
149
+ setElapsedTime ( event . value || 0 ) ;
145
150
}
146
151
) ;
147
152
@@ -183,16 +188,6 @@ const AudioFile: FC = () => {
183
188
step = { 0.25 }
184
189
minLabelWidth = { labelWidth }
185
190
/>
186
- < Spacer . Vertical size = { 20 } />
187
- < Slider
188
- label = "Detune"
189
- value = { detune }
190
- onValueChange = { handleDetuneChange }
191
- min = { - 1200 }
192
- max = { 1200 }
193
- step = { 100 }
194
- minLabelWidth = { labelWidth }
195
- />
196
191
</ Container >
197
192
) ;
198
193
} ;
0 commit comments