3
3
import MicRecorder from 'mic-recorder-to-mp3' ;
4
4
import { useEffect , useRef , useState } from 'react' ;
5
5
import Button from 'react-bootstrap/Button' ;
6
- import { FaMicrophone , FaStop , FaCloudUploadAlt , FaSpinner , FaTimesCircle , FaCheck } from 'react-icons/fa' ;
6
+ import {
7
+ FaMicrophone , FaStop , FaCloudUploadAlt ,
8
+ FaSpinner , FaTimesCircle , FaCheck , FaPlay , FaPause ,
9
+ FaVolumeOff , FaVolumeMute , FaVolumeDown , FaVolumeUp , FaRegTrashAlt
10
+ } from 'react-icons/fa' ;
7
11
import { useDispatch , useSelector } from 'react-redux' ;
8
12
import ListGroup from 'react-bootstrap/ListGroup' ;
9
13
import ListGroupItem from 'react-bootstrap/ListGroupItem' ;
@@ -12,6 +16,128 @@ import Row from 'react-bootstrap/Row';
12
16
import { useRouter } from 'next/router' ;
13
17
import { UploadStatusEnum } from '../types' ;
14
18
import StatusIndicator from './statusIndicator' ;
19
+ import WaveSurfer from 'wavesurfer.js' ;
20
+ import styles from '../styles/recorder.module.css' ;
21
+
22
+ function AudioViewer ( { src } ) {
23
+ const containerW = useRef ( null ) ;
24
+ const waveSurf = useRef ( null ) ;
25
+ const volume = useRef ( null ) ;
26
+ const play = < FaPlay style = { { paddingLeft : '2px' } } /> ;
27
+ const pause = < FaPause /> ;
28
+ const vMute = < FaVolumeMute style = { { width : '1.05em' , height : '1.05em' , cursor : 'pointer' , color : 'red' , paddingLeft : '2px' } } onClick = { toggleVolume } /> ;
29
+ const vOff = < FaVolumeOff style = { { cursor : 'pointer' , paddingRight : '9px' } } onClick = { toggleVolume } /> ;
30
+ const vDown = < FaVolumeDown style = { { cursor : 'pointer' , paddingRight : '3px' } } onClick = { toggleVolume } /> ;
31
+ const vUp = < FaVolumeUp style = { { width : '1.23em' , height : '1.23em' , cursor : 'pointer' , paddingLeft : '3px' } } onClick = { toggleVolume } /> ;
32
+ const [ playing , setPlay ] = useState ( play ) ;
33
+ const [ volumeIndex , changeVolume ] = useState ( vUp ) ;
34
+
35
+ useEffect ( ( ) => {
36
+ if ( containerW . current && ! waveSurf . current ) {
37
+ waveSurf . current = WaveSurfer . create ( {
38
+ container : containerW . current ,
39
+ waveColor : 'blue' ,
40
+ progressColor : 'purple' ,
41
+ barWidth : 3 ,
42
+ barHeight : 0.5 ,
43
+ barRadius : 3 ,
44
+ cursorWidth : 3 ,
45
+ height : 200 ,
46
+ barGap : 3 ,
47
+ dragToSeek : true
48
+ // plugins:[
49
+ // WaveSurferRegions.create({maxLength: 60}),
50
+ // WaveSurferTimeLinePlugin.create({container: containerT.current})
51
+ // ]
52
+ } ) ;
53
+ if ( waveSurf . current ) {
54
+ waveSurf . current . load ( src ) ;
55
+ }
56
+ if ( volume . current && waveSurf . current ) {
57
+ waveSurf . current . setVolume ( volume . current . value ) ;
58
+ volume . current . addEventListener ( 'input' , handleVolumeChange ) ;
59
+ }
60
+ }
61
+ } , [ ] ) ;
62
+
63
+ function handleVolumeChange ( ) {
64
+ waveSurf . current . setVolume ( volume . current . value ) ;
65
+ let volumeNum = volume . current . value * 100 ;
66
+ volume . current . style . setProperty ( '--volumePercent' , volumeNum + '%' ) ;
67
+ if ( volume . current . value == 0 ) {
68
+ changeVolume ( vMute ) ;
69
+ }
70
+ else if ( volume . current . value < .25 ) {
71
+ changeVolume ( vOff ) ;
72
+ }
73
+ else if ( volume . current . value < .5 ) {
74
+ changeVolume ( vDown ) ;
75
+ }
76
+ else if ( volume . current . value < .75 ) {
77
+ changeVolume ( vUp ) ;
78
+ }
79
+ }
80
+
81
+ function toggleVolume ( ) {
82
+ if ( volume . current ) {
83
+ if ( volume . current . value != 0 ) {
84
+ volume . current . value = 0 ;
85
+ waveSurf . current . setVolume ( volume . current . value ) ;
86
+ volume . current . style . setProperty ( '--volumePercent' , 0 + '%' ) ;
87
+ changeVolume ( vMute ) ;
88
+ }
89
+ else {
90
+ volume . current . value = 1 ;
91
+ waveSurf . current . setVolume ( volume . current . value ) ;
92
+ volume . current . style . setProperty ( '--volumePercent' , 100 + '%' ) ;
93
+ changeVolume ( vUp ) ;
94
+ }
95
+ }
96
+ }
97
+
98
+ function playPause ( ) {
99
+ if ( waveSurf . current . isPlaying ( ) ) {
100
+ setPlay ( play ) ;
101
+ waveSurf . current . pause ( ) ;
102
+ }
103
+ else {
104
+ setPlay ( pause ) ;
105
+ waveSurf . current . play ( ) ;
106
+ }
107
+ } ;
108
+ if ( waveSurf . current ) {
109
+ waveSurf . current . on ( 'finish' , ( ) => {
110
+ setPlay ( play ) ;
111
+ } ) ;
112
+ }
113
+
114
+ return (
115
+ < div style = { {
116
+ width : '100%' ,
117
+ display : 'flex' ,
118
+ flexDirection : 'column' ,
119
+ justifyContent : 'center' ,
120
+ alignItems : 'center' ,
121
+ margin : '0 1rem 0 1rem'
122
+ } } >
123
+ < div className = { styles . waveContainer } ref = { containerW } style = { { width : '100%' } } > </ div >
124
+ < div style = { { display : 'flex' , justifyContent : 'center' , alignItems : 'center' } } >
125
+ < Button style = { {
126
+ marginRight : '1rem' ,
127
+ display : 'flex' ,
128
+ justifyContent : 'center' ,
129
+ alignItems : 'center' ,
130
+ width : '40px' ,
131
+ height : '40px' ,
132
+ borderRadius : '50%' ,
133
+ padding : '0'
134
+ } } onClick = { playPause } > { playing } </ Button >
135
+ < input className = { styles . slider } style = { { marginRight : '1.5rem' } } ref = { volume } type = "range" min = "0" max = "1" step = "0.01" defaultValue = "1" > </ input >
136
+ { volumeIndex }
137
+ </ div >
138
+ </ div >
139
+ ) ;
140
+ }
15
141
16
142
export default function Recorder ( { submit, accompaniment } ) {
17
143
// const Mp3Recorder = new MicRecorder({ bitRate: 128 }); // 128 is default already
@@ -43,7 +169,7 @@ export default function Recorder({ submit, accompaniment }) {
43
169
accompanimentRef . current . play ( ) ;
44
170
recorder
45
171
. start ( )
46
- . then ( ( ) => {
172
+ . then ( ( ) => {
47
173
setIsRecording ( true ) ;
48
174
} )
49
175
. catch ( ( err ) => console . error ( 'problem starting recording' , err ) ) ;
@@ -53,7 +179,7 @@ export default function Recorder({ submit, accompaniment }) {
53
179
const stopRecording = ( ev ) => {
54
180
accompanimentRef . current . pause ( ) ;
55
181
accompanimentRef . current . load ( ) ;
56
-
182
+
57
183
recorder
58
184
. stop ( )
59
185
. getMp3 ( )
@@ -85,6 +211,12 @@ export default function Recorder({ submit, accompaniment }) {
85
211
submit ( { audio : formData , submissionId } ) ;
86
212
} ;
87
213
214
+ function deleteTake ( index ) {
215
+ let newInfo = blobInfo . slice ( ) ;
216
+ newInfo . splice ( index , 1 ) ;
217
+ setBlobInfo ( newInfo ) ;
218
+ }
219
+
88
220
// check for recording permissions
89
221
useEffect ( ( ) => {
90
222
if (
@@ -93,7 +225,7 @@ export default function Recorder({ submit, accompaniment }) {
93
225
navigator . mediaDevices . getUserMedia
94
226
) {
95
227
navigator . mediaDevices
96
- . getUserMedia ( { audio : { echoCancellation :false , noiseSuppression : false } } )
228
+ . getUserMedia ( { audio : { echoCancellation : false , noiseSuppression : false } } )
97
229
. then ( ( ) => {
98
230
setIsBlocked ( false ) ;
99
231
} )
@@ -163,16 +295,23 @@ export default function Recorder({ submit, accompaniment }) {
163
295
style = { { fontSize : '1.5rem' } }
164
296
>
165
297
{ /* eslint-disable-next-line jsx-a11y/media-has-caption */ }
166
- < audio
298
+ { /* <audio
167
299
style={{ height: '2.25rem' }}
168
300
src={take.url}
169
301
controls
170
- />
171
- < Button
172
- onClick = { ( ) => submitRecording ( i , `recording-take-${ i } ` ) }
173
- >
174
- < FaCloudUploadAlt />
175
- </ Button >
302
+ /> */ }
303
+ < AudioViewer src = { take . url } />
304
+ < div >
305
+ < Button
306
+ onClick = { ( ) => submitRecording ( i , `recording-take-${ i } ` ) }
307
+ >
308
+ < FaCloudUploadAlt />
309
+ </ Button >
310
+ < Button
311
+ onClick = { ( ) => deleteTake ( i ) } >
312
+ < FaRegTrashAlt />
313
+ </ Button >
314
+ </ div >
176
315
< div className = "minWidth" >
177
316
< StatusIndicator statusId = { `recording-take-${ i } ` } />
178
317
</ div >
0 commit comments