Skip to content

Commit a70c222

Browse files
authored
Merge pull request #14 from Lab-Lab-Lab/wavesurfer
Wavesurfer addition
2 parents b7a8d00 + 3c199dd commit a70c222

File tree

4 files changed

+200
-13
lines changed

4 files changed

+200
-13
lines changed

components/recorder.js

+150-11
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
import MicRecorder from 'mic-recorder-to-mp3';
44
import { useEffect, useRef, useState } from 'react';
55
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';
711
import { useDispatch, useSelector } from 'react-redux';
812
import ListGroup from 'react-bootstrap/ListGroup';
913
import ListGroupItem from 'react-bootstrap/ListGroupItem';
@@ -12,6 +16,128 @@ import Row from 'react-bootstrap/Row';
1216
import { useRouter } from 'next/router';
1317
import { UploadStatusEnum } from '../types';
1418
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+
}
15141

16142
export default function Recorder({ submit, accompaniment }) {
17143
// const Mp3Recorder = new MicRecorder({ bitRate: 128 }); // 128 is default already
@@ -43,7 +169,7 @@ export default function Recorder({ submit, accompaniment }) {
43169
accompanimentRef.current.play();
44170
recorder
45171
.start()
46-
.then(()=>{
172+
.then(() => {
47173
setIsRecording(true);
48174
})
49175
.catch((err) => console.error('problem starting recording', err));
@@ -53,7 +179,7 @@ export default function Recorder({ submit, accompaniment }) {
53179
const stopRecording = (ev) => {
54180
accompanimentRef.current.pause();
55181
accompanimentRef.current.load();
56-
182+
57183
recorder
58184
.stop()
59185
.getMp3()
@@ -85,6 +211,12 @@ export default function Recorder({ submit, accompaniment }) {
85211
submit({ audio: formData, submissionId });
86212
};
87213

214+
function deleteTake(index) {
215+
let newInfo = blobInfo.slice();
216+
newInfo.splice(index, 1);
217+
setBlobInfo(newInfo);
218+
}
219+
88220
// check for recording permissions
89221
useEffect(() => {
90222
if (
@@ -93,7 +225,7 @@ export default function Recorder({ submit, accompaniment }) {
93225
navigator.mediaDevices.getUserMedia
94226
) {
95227
navigator.mediaDevices
96-
.getUserMedia({ audio: {echoCancellation:false, noiseSuppression: false} })
228+
.getUserMedia({ audio: { echoCancellation: false, noiseSuppression: false } })
97229
.then(() => {
98230
setIsBlocked(false);
99231
})
@@ -163,16 +295,23 @@ export default function Recorder({ submit, accompaniment }) {
163295
style={{ fontSize: '1.5rem' }}
164296
>
165297
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
166-
<audio
298+
{/* <audio
167299
style={{ height: '2.25rem' }}
168300
src={take.url}
169301
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>
176315
<div className="minWidth">
177316
<StatusIndicator statusId={`recording-take-${i}`} />
178317
</div>

package-lock.json

+8-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"redux": "^4.0.4",
2727
"redux-devtools-extension": "^2.13.8",
2828
"redux-thunk": "^2.3.0",
29-
"swr": "^1.1.2"
29+
"swr": "^1.1.2",
30+
"wavesurfer.js": "^7.7.15"
3031
},
3132
"devDependencies": {
3233
"eslint": "^8.7.0",

styles/recorder.module.css

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
.slider{
2+
appearance: none;
3+
width: 200px;
4+
height: 10px;
5+
background-color: purple;
6+
border-radius: 12px;
7+
padding: 0;
8+
--volumePercent: 100%;
9+
background: linear-gradient(90deg, rgb(4, 255, 0) var(--volumePercent), gray 0%);
10+
}
11+
12+
.slider::-webkit-slider-thumb{
13+
appearance: none;
14+
width: 25px;
15+
height: 25px;
16+
border-radius: 50%;
17+
background-color: black;
18+
cursor: pointer;
19+
}
20+
21+
.slider::-moz-range-thumb{
22+
width: 25px;
23+
height: 25px;
24+
border-radius: 50%;
25+
background-color: black;
26+
cursor: pointer;
27+
}
28+
29+
.waveContainer ::part(cursor){
30+
height: 150px;
31+
top: 50%;
32+
transform: translateY(-50%);
33+
}
34+
35+
36+
@media screen and (max-width: 600px) {
37+
.slider{
38+
width: 100px;
39+
}
40+
}

0 commit comments

Comments
 (0)