|
1
|
/* eslint-disable no-script-url */ |
|
2
|
/* eslint-disable jsx-a11y/anchor-is-valid */ |
|
3
|
import axios from 'axios'; |
|
4
|
import React from 'react'; |
|
5
|
import { Component } from 'react'; |
|
6
|
import { Col, Row } from 'react-bootstrap'; |
|
7
|
import YouTube, { Options } from 'react-youtube'; |
|
8
|
import { GiMicrophone, GiDrumKit, GiGuitarHead, GiGuitarBassHead, GiPianoKeys } from 'react-icons/gi'; |
|
9
|
import { IconContext } from 'react-icons'; |
|
10
|
import { db, AudioFiles } from './db'; |
|
11
|
|
|
12
|
interface Result { |
|
13
|
source_file: string, |
|
14
|
filename: string |
|
15
|
} |
|
16
|
|
|
17
|
interface AudioFile { |
|
18
|
youtube_video_id: string, |
|
19
|
results: Result[] |
|
20
|
} |
|
21
|
|
|
22
|
type AudioProps = { |
|
23
|
audioFileId: string |
|
24
|
} |
|
25
|
|
|
26
|
type AudioState = { |
|
27
|
audioFile: AudioFile | null, |
|
28
|
isPlayerReady: boolean, |
|
29
|
isPlaying: boolean |
|
30
|
vocalAudioOn: boolean |
|
31
|
guitarAudioOn: boolean |
|
32
|
bassAudioOn: boolean |
|
33
|
drumsAudioOn: boolean |
|
34
|
pianoAudioOn: boolean |
|
35
|
} |
|
36
|
|
|
37
|
export enum Mode { |
|
38
|
vocalist, bassist, guitarist, drummer, keyboardist |
|
39
|
} |
|
40
|
|
|
41
|
class AudioPlayer extends Component<AudioProps, AudioState> { |
|
42
|
|
|
43
|
vocalAudio = new Audio(); |
|
44
|
guitarAudio = new Audio(); |
|
45
|
bassAudio = new Audio(); |
|
46
|
drumsAudio = new Audio(); |
|
47
|
pianoAudio = new Audio() |
|
48
|
|
|
49
|
constructor(props: AudioProps) { |
|
50
|
super(props); |
|
51
|
this.state = { |
|
52
|
audioFile: null, |
|
53
|
isPlayerReady: false, |
|
54
|
isPlaying: false, |
|
55
|
vocalAudioOn: false, |
|
56
|
guitarAudioOn: false, |
|
57
|
bassAudioOn: false, |
|
58
|
drumsAudioOn: false, |
|
59
|
pianoAudioOn: false, |
|
60
|
} |
|
61
|
} |
|
62
|
|
|
63
|
componentDidMount() { |
|
64
|
axios.get(`/splitfire/${this.props.audioFileId}`) |
|
65
|
.then(res => { |
|
66
|
console.log("Get results", res); |
|
67
|
const audioFile = res.data.audio_file; |
|
68
|
this.setState({ audioFile }); |
|
69
|
console.log(this.state); |
|
70
|
this.downloadAudioFilesIfNeeded(); |
|
71
|
}) |
|
72
|
} |
|
73
|
|
|
74
|
downloadAudioFilesIfNeeded() { |
|
75
|
this._prepareAudio(Mode.vocalist); |
|
76
|
this._prepareAudio(Mode.bassist); |
|
77
|
this._prepareAudio(Mode.drummer); |
|
78
|
this._prepareAudio(Mode.guitarist); |
|
79
|
this._prepareAudio(Mode.keyboardist); |
|
80
|
} |
|
81
|
|
|
82
|
async _downloadFile(type: Mode) { |
|
83
|
console.log('_downloadFile', type); |
|
84
|
let audioFile: Result | undefined; |
|
85
|
switch (type) { |
|
86
|
case Mode.vocalist: |
|
87
|
audioFile = this.state.audioFile?.results.find(obj => { return obj.filename.startsWith('vocals') }); |
|
88
|
break; |
|
89
|
case Mode.bassist: |
|
90
|
audioFile = this.state.audioFile?.results.find(obj => { return obj.filename.startsWith('bass') }); |
|
91
|
break; |
|
92
|
case Mode.drummer: |
|
93
|
audioFile = this.state.audioFile?.results.find(obj => { return obj.filename.startsWith('drums') }); |
|
94
|
break; |
|
95
|
case Mode.guitarist: |
|
96
|
audioFile = this.state.audioFile?.results.find(obj => { return obj.filename.startsWith('other') }); |
|
97
|
break; |
|
98
|
case Mode.keyboardist: |
|
99
|
audioFile = this.state.audioFile?.results.find(obj => { return obj.filename.startsWith('piano') }); |
|
100
|
break; |
|
101
|
} |
|
102
|
|
|
103
|
const audioFileId = this.props.audioFileId; |
|
104
|
console.log("Download url", audioFile?.source_file); |
|
105
|
axios({ |
|
106
|
url: audioFile?.source_file, |
|
107
|
method: 'GET', |
|
108
|
responseType: 'blob', |
|
109
|
}).then((response) => { |
|
110
|
const file = new Blob([response.data], { type: 'audio/mp3' }); |
|
111
|
const item: AudioFiles = { |
|
112
|
audioFileId, type: type, file |
|
113
|
} |
|
114
|
db.audioFiles.add(item); |
|
115
|
console.log('File Downloaded', file); |
|
116
|
this._setAudioSrc(type, file); |
|
117
|
}).catch(error => { |
|
118
|
console.log(error); |
|
119
|
}); |
|
120
|
} |
|
121
|
|
|
122
|
async _prepareAudio(type: Mode) { |
|
123
|
const audioFileId = this.props.audioFileId; |
|
124
|
db.audioFiles |
|
125
|
.get({ audioFileId: audioFileId, type: type }) |
|
126
|
.then(record => { |
|
127
|
if (record?.file === undefined) { |
|
128
|
this._downloadFile(type); |
|
129
|
return |
|
130
|
} |
|
131
|
this._setAudioSrc(type, record.file) |
|
132
|
}) |
|
133
|
.catch(onrejected => { |
|
134
|
console.log("Rejected", onrejected); |
|
135
|
}); |
|
136
|
} |
|
137
|
|
|
138
|
_setAudioSrc(type: Mode, file: Blob) { |
|
139
|
// Get window.URL object |
|
140
|
var URL = window.URL || window.webkitURL; |
|
141
|
// Create and revoke ObjectURL |
|
142
|
var audioFileURL = URL.createObjectURL(file); |
|
143
|
|
|
144
|
switch (type) { |
|
145
|
case Mode.vocalist: |
|
146
|
this.vocalAudio.src = audioFileURL; |
|
147
|
break; |
|
148
|
case Mode.bassist: |
|
149
|
this.bassAudio.src = audioFileURL; |
|
150
|
break; |
|
151
|
case Mode.drummer: |
|
152
|
this.drumsAudio.src = audioFileURL; |
|
153
|
break; |
|
154
|
case Mode.guitarist: |
|
155
|
this.guitarAudio.src = audioFileURL; |
|
156
|
break; |
|
157
|
case Mode.keyboardist: |
|
158
|
this.pianoAudio.src = audioFileURL; |
|
159
|
break; |
|
160
|
} |
|
161
|
} |
|
162
|
|
|
163
|
playAudio() { |
|
164
|
this.vocalAudio.play(); |
|
165
|
this.guitarAudio.play(); |
|
166
|
this.bassAudio.play(); |
|
167
|
this.guitarAudio.play(); |
|
168
|
this.drumsAudio.play(); |
|
169
|
} |
|
170
|
|
|
171
|
pauseAudio() { |
|
172
|
this.vocalAudio.pause(); |
|
173
|
this.guitarAudio.pause(); |
|
174
|
this.bassAudio.pause(); |
|
175
|
this.guitarAudio.pause(); |
|
176
|
this.drumsAudio.pause(); |
|
177
|
} |
|
178
|
|
|
179
|
setToggleInstrument(mode: Mode) { |
|
180
|
switch (mode) { |
|
181
|
case Mode.vocalist: |
|
182
|
this.setState({vocalAudioOn: !this.state.vocalAudioOn}); |
|
183
|
this.vocalAudio.volume = this.state.vocalAudioOn ? 1 : 0; |
|
184
|
break; |
|
185
|
case Mode.bassist: |
|
186
|
this.setState({bassAudioOn: !this.state.bassAudioOn}); |
|
187
|
this.bassAudio.volume = this.state.bassAudioOn ? 1 : 0;; |
|
188
|
break; |
|
189
|
case Mode.guitarist: |
|
190
|
this.setState({guitarAudioOn: !this.state.guitarAudioOn}); |
|
191
|
this.guitarAudio.volume = this.state.guitarAudioOn ? 1 : 0;; |
|
192
|
break; |
|
193
|
case Mode.keyboardist: |
|
194
|
this.setState({pianoAudioOn: !this.state.pianoAudioOn}); |
|
195
|
this.pianoAudio.volume = this.state.pianoAudioOn ? 1 : 0;; |
|
196
|
break; |
|
197
|
case Mode.drummer: |
|
198
|
this.setState({drumsAudioOn: !this.state.drumsAudioOn}); |
|
199
|
this.drumsAudio.volume = this.state.drumsAudioOn ? 1 : 0;; |
|
200
|
break; |
|
201
|
} |
|
202
|
} |
|
203
|
|
|
204
|
render() { |
|
205
|
const opts: Options = { |
|
206
|
width: '100%', |
|
207
|
playerVars: { |
|
208
|
// https://developers.google.com/youtube/player_parameters |
|
209
|
autoplay: 0, |
|
210
|
mute: 1, |
|
211
|
controls: 0, |
|
212
|
rel: 0, |
|
213
|
showinfo: 0 |
|
214
|
}, |
|
215
|
}; |
|
216
|
|
|
217
|
let youtubeContent: any = 'Loading player...'; |
|
218
|
if (this.state.audioFile) { |
|
219
|
youtubeContent = <YouTube |
|
220
|
videoId={this.state.audioFile?.youtube_video_id} |
|
221
|
opts={opts} |
|
222
|
onReady={this._onReady.bind(this)} |
|
223
|
onStateChange={this._onStateChange.bind(this)} |
|
224
|
onPlay={this._onPlay.bind(this)} |
|
225
|
onPause={this._onPause.bind(this)} |
|
226
|
/> |
|
227
|
} |
|
228
|
|
|
229
|
return ( |
|
230
|
<IconContext.Provider value={{ size: "2em", color: "white", className: "global-class-name" }}> |
|
231
|
<Row className="mb-3 mt-3"> |
|
232
|
<Col> |
|
233
|
<a href='javascript:void(0)' onClick={() => this.setToggleInstrument(Mode.vocalist)} > |
|
234
|
<GiMicrophone color={this.state.vocalAudioOn ? `white` : `red`} /> |
|
235
|
</a> |
|
236
|
</Col> |
|
237
|
<Col> |
|
238
|
<a href='javascript:void(0)' onClick={() => this.setToggleInstrument(Mode.drummer)} > |
|
239
|
<GiDrumKit color={this.state.drumsAudioOn ? `white` : `red`} /> |
|
240
|
</a> |
|
241
|
</Col> |
|
242
|
<Col> |
|
243
|
<a href='javascript:void(0)' onClick={() => this.setToggleInstrument(Mode.bassist)} > |
|
244
|
<GiGuitarBassHead color={this.state.bassAudioOn ? `white` : `red`} /> |
|
245
|
</a> |
|
246
|
</Col> |
|
247
|
<Col> |
|
248
|
<a href='javascript:void(0)' onClick={() => this.setToggleInstrument(Mode.guitarist)} > |
|
249
|
<GiGuitarHead color={this.state.guitarAudioOn ? `white` : `red`} /> |
|
250
|
</a> |
|
251
|
</Col> |
|
252
|
<Col> |
|
253
|
<a href='javascript:void(0)' onClick={() => this.setToggleInstrument(Mode.keyboardist)} > |
|
254
|
<GiPianoKeys color={this.state.pianoAudioOn ? `white` : `red`} /> |
|
255
|
</a> |
|
256
|
</Col> |
|
257
|
</Row> |
|
258
|
<Row> |
|
259
|
<Col> |
|
260
|
{youtubeContent} |
|
261
|
</Col> |
|
262
|
</Row> |
|
263
|
</IconContext.Provider> |
|
264
|
) |
|
265
|
} |
|
266
|
|
|
267
|
_onReady(event: any) { |
|
268
|
console.log('YouTube ready: ', event) |
|
269
|
} |
|
270
|
|
|
271
|
_onStateChange(event: any) { |
|
272
|
console.log('State changed: ', event) |
|
273
|
} |
|
274
|
|
|
275
|
_onPlay() { |
|
276
|
this.playAudio() |
|
277
|
} |
|
278
|
_onPause() { |
|
279
|
this.pauseAudio() |
|
280
|
} |
|
281
|
} |
|
282
|
|
|
283
|
export default AudioPlayer; |