fix player
Seto Elkahfi committed
Aug 1, 2024 at 21:08 UTC
ebe348aa6ed04f36784f450693fc98f1a7e56aaa
3 files changed
+264
-6
frontend/splitfire-desktop/app/_src/components/player/Player.tsx
+2
-2
@@ -22,14 +22,14 @@ enum State {
22
ERROR,
23
}
24
25
-enum PlayerState {
25
+export enum PlayerState {
26
STOPPED,
27
PLAYING,
28
PAUSED,
29
RECORDING
30
}
31
32
-interface PlayerVolume {
32
+export interface PlayerVolume {
33
mode: ModeDemucs,
34
volume: string
35
}
frontend/splitfire-desktop/app/play/_components/player.tsx
+249
-2
@@ -1,9 +1,256 @@
1
+"use client";
2
+
3
+import { ControlButtonsView } from "@/app/_src/components/player/ControlButtons";
4
+import { CountDownView } from "@/app/_src/components/player/CountDownView";
5
+import { HideShowToggleView } from "@/app/_src/components/player/HideShowToggle";
6
+import { ModeDemucs } from "@/app/_src/components/player/models/Mode";
7
+import { PlayerState, PlayerVolume } from "@/app/_src/components/player/Player";
8
+import { RecordingView } from "@/app/_src/components/player/RecordingView";
9
+import { VolumeSliderView } from "@/app/_src/components/player/VolumeSliderView";
10
+import { useLogger } from "@/app/_src/lib/logger";
11
+import {
12
+ TAURI_PLAYER_PAUSED,
13
+ TAURI_PLAYER_PLAY,
14
+ TAURI_PLAYER_RECORD,
15
+ TAURI_PLAYER_RECORD_STOP,
16
+ TAURI_PLAYER_RECORDING_LENGTH,
17
+ TAURI_PLAYER_RESUMED,
18
+ TAURI_PLAYER_SET_VOLUME,
19
+ TAURI_PLAYER_STOP,
20
+} from "@/app/_src/lib/tauriHandler";
21
+import { invoke } from "@tauri-apps/api";
22
+import { useState } from "react";
23
+import { Row, Col, Container } from "react-bootstrap";
24
+
25
+export default function Player({
26
+ audioId,
27
+ userId,
28
+}: {
29
+ audioId: string;
30
+ userId: number;
31
+}) {
32
+ // Logger
33
+ const log = useLogger("Player");
34
+
35
+ // State and constants
36
+ const COUNTING_DOWN_NUMBER = 3;
37
+ const [isCountingCountdown, setIsCountingCountdown] = useState(false);
38
+ const [recordingLength, setRecordingLength] = useState<number>(0);
39
+ const [hideVolumeSliders, setHideVolumeSliders] = useState(false);
40
+ const [playerState, setPlayerState] = useState<PlayerState>(
41
+ PlayerState.STOPPED
42
+ );
43
+
44
+ // Player states
45
+ const [vocalsVolume, setVocalsVolume] = useState<string>("100");
46
+ const [drumsVolume, setDrumsVolume] = useState<string>("100");
47
+ const [bassVolume, setBassVolume] = useState<string>("100");
48
+ const [otherVolume, setOtherVolume] = useState<string>("100");
49
+
50
+ // Player functions
51
+ const _onVolumeChange = (mode: ModeDemucs, value: string) => {
52
+ log.debug("_onVolumeChange", mode, value);
53
+ switch (mode) {
54
+ case ModeDemucs.Vocals:
55
+ setVocalsVolume(value);
56
+ break;
57
+ case ModeDemucs.Drums:
58
+ setDrumsVolume(value);
59
+ break;
60
+ case ModeDemucs.Bass:
61
+ setBassVolume(value);
62
+ break;
63
+ case ModeDemucs.Other:
64
+ setOtherVolume(value);
65
+ break;
66
+ }
67
+ invoke(TAURI_PLAYER_SET_VOLUME, { mode: mode, volume: value });
68
+ };
69
+
70
+ const onFinishedRecording = () => {
71
+ log.debug("Recording finished");
72
+ //_toggleRecording()
73
+ };
74
+
75
+ const _toggleRecording = async () => {
76
+ try {
77
+ // If we are playing playbacks, stop it before recording.
78
+ if (playerState === PlayerState.PLAYING) {
79
+ setPlayerState(PlayerState.STOPPED);
80
+ const result = await invoke(TAURI_PLAYER_STOP, {
81
+ audioId,
82
+ userId,
83
+ });
84
+ log.debug(TAURI_PLAYER_STOP, result);
85
+ }
86
+
87
+ // If we're already recording, we want to stop it and return.
88
+ if (playerState === PlayerState.RECORDING) {
89
+ _stopRecording();
90
+ return;
91
+ }
92
+
93
+ // Otherwise, we're good to go.
94
+ // Get the length of the audio file.
95
+
96
+ let length: number = await invoke(TAURI_PLAYER_RECORDING_LENGTH, {
97
+ audioId,
98
+ userId,
99
+ });
100
+ log.debug(TAURI_PLAYER_RECORDING_LENGTH, length);
101
+ setRecordingLength(length);
102
+ setIsCountingCountdown(true);
103
+
104
+ setTimeout(async () => {
105
+ log.debug(TAURI_PLAYER_RECORD, "Start recording");
106
+ setPlayerState(PlayerState.RECORDING);
107
+ setIsCountingCountdown(false);
108
+ const result = await invoke(TAURI_PLAYER_RECORD, {
109
+ audioId,
110
+ userId,
111
+ playerVolumes: _playerVolumes(),
112
+ });
113
+ log.debug(TAURI_PLAYER_RECORD, result);
114
+ }, COUNTING_DOWN_NUMBER * 1000);
115
+ } catch (error) {
116
+ log.error(error);
117
+ }
118
+ };
119
+
120
+ const _stopRecording = async () => {
121
+ setRecordingLength(0);
122
+ setPlayerState(PlayerState.STOPPED);
123
+ const stop_recording_result = await invoke(TAURI_PLAYER_RECORD_STOP, {
124
+ audioId,
125
+ userId,
126
+ });
127
+ const result = await invoke(TAURI_PLAYER_STOP, {
128
+ audioId,
129
+ userId,
130
+ });
131
+ log.debug(TAURI_PLAYER_STOP, result);
132
+ log.debug(TAURI_PLAYER_RECORD_STOP, stop_recording_result);
133
+ };
134
+
135
+ const _stopPlayer = async () => {
136
+ try {
137
+ setPlayerState(PlayerState.STOPPED);
138
+ setRecordingLength(0);
139
+ const result = await invoke(TAURI_PLAYER_STOP, {
140
+ audioId,
141
+ userId,
142
+ });
143
+ log.debug(TAURI_PLAYER_STOP, result);
144
+ } catch (error) {
145
+ log.error(error);
146
+ }
147
+ };
148
+ const _playerVolumes = (): PlayerVolume[] => {
149
+ return [
150
+ { mode: ModeDemucs.Vocals, volume: vocalsVolume },
151
+ { mode: ModeDemucs.Drums, volume: drumsVolume },
152
+ { mode: ModeDemucs.Bass, volume: bassVolume },
153
+ { mode: ModeDemucs.Other, volume: otherVolume },
154
+ ];
155
+ };
156
+
157
+ const _togglePlayAudio = async () => {
158
+ // Should disabled when recording
159
+ if (playerState === PlayerState.RECORDING) {
160
+ return;
161
+ }
162
+ try {
163
+ switch (playerState) {
164
+ case PlayerState.STOPPED:
165
+ log.debug(TAURI_PLAYER_PLAY, "Start playing");
166
+ setIsCountingCountdown(true);
167
+ setTimeout(async () => {
168
+ setIsCountingCountdown(false);
169
+ setPlayerState(PlayerState.PLAYING);
170
+ await invoke(TAURI_PLAYER_PLAY, {
171
+ playerVolumes: _playerVolumes(),
172
+ });
173
+ }, COUNTING_DOWN_NUMBER * 1000);
174
+ break;
175
+ case PlayerState.PLAYING:
176
+ log.debug(TAURI_PLAYER_PAUSED, "Paused");
177
+ setPlayerState(PlayerState.PAUSED);
178
+ await invoke(TAURI_PLAYER_PAUSED);
179
+ break;
180
+ case PlayerState.PAUSED:
181
+ log.debug(TAURI_PLAYER_RESUMED, "Resumed");
182
+ setPlayerState(PlayerState.PLAYING);
183
+ await invoke(TAURI_PLAYER_PLAY, { playerVolumes: _playerVolumes() });
184
+ }
185
+ } catch (error) {
186
+ log.error(error);
187
+ }
188
+ };
189
+
190
+ let buttonOrCounting = <></>;
191
+ if (isCountingCountdown) {
192
+ buttonOrCounting = <CountDownView seconds={COUNTING_DOWN_NUMBER} type="" />;
193
+ } else {
194
+ buttonOrCounting = (
195
+ <ControlButtonsView
196
+ isPlaying={playerState === PlayerState.PLAYING}
197
+ isRecording={playerState === PlayerState.RECORDING}
198
+ onClick={_togglePlayAudio}
199
+ onStop={_stopPlayer}
200
+ onRecord={_toggleRecording}
201
+ />
202
+ );
203
+ }
204
+
205
+ let audioWaveform = <></>;
206
+ if (
207
+ playerState === PlayerState.PLAYING ||
208
+ playerState === PlayerState.PAUSED
209
+ ) {
210
+ audioWaveform = (
211
+ <>
212
+ <CountDownView seconds={recordingLength} type="Practicing!!!" />
213
+ </>
214
+ );
215
+ } else if (playerState === PlayerState.RECORDING) {
216
+ audioWaveform = (
217
+ <RecordingView
218
+ length={recordingLength}
219
+ onRecordingEnd={onFinishedRecording}
220
+ />
221
+ );
222
+ }
223
2
-export default async function Page() {
224
return (
225
<div className="prose prose-sm prose-invert max-w-none">
226
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
6
- Splitting file
227
+ <Container>
228
+ {buttonOrCounting}
229
+ <Row className="h-100 d-inline-block">
230
+ <Col xs={12} className="mb-3 mt-3">
231
+ <Container style={{ height: 300 }}>
232
+ <div className="d-flex align-items-center justify-content-center h-100">
233
+ <div className="d-flex flex-column">{audioWaveform}</div>
234
+ </div>
235
+ </Container>
236
+ </Col>
237
+ </Row>
238
+ <Row className="border border-light">
239
+ <Col xs={2} className="mb-3 mt-3">
240
+ <p color="red">Volume</p>
241
+ </Col>
242
+ <Col xs={{ span: 2, offset: 8 }} className="mb-3 mt-3">
243
+ <HideShowToggleView
244
+ hideVolumeSliders={hideVolumeSliders}
245
+ setHideVolumeSliders={setHideVolumeSliders}
246
+ />
247
+ </Col>
248
+ <VolumeSliderView
249
+ _onVolumeChange={_onVolumeChange}
250
+ isHidden={hideVolumeSliders}
251
+ />
252
+ </Row>
253
+ </Container>
254
</div>
255
</div>
256
);
frontend/splitfire-desktop/app/play/page.tsx
+13
-2
@@ -1,13 +1,15 @@
1
"use client";
2
3
import { invoke } from "@tauri-apps/api";
4
-import { useEffect, useState } from "react";
4
+import { useContext, useEffect, useState } from "react";
5
import { TAURI_PLAYER_PREPARE } from "../_src/lib/tauriHandler";
6
import { useSearchParams } from "next/navigation";
7
import { useLogger } from "../_src/lib/logger";
8
import { IconSpinner } from "../_ui/components/icons";
9
import { PlayerPrepareResponse } from "@/models/content";
10
import { TauriResponse } from "@/models/shared";
11
+import Player from "./_components/player";
12
+import { UserContext } from "../_src/lib/CurrentUserContext";
13
14
enum State {
15
LOADING,
@@ -21,6 +23,8 @@ export default function Page() {
23
const audioFileId = searchParams.get("audioFileId");
24
const [title, setTitle] = useState("Loading song...");
25
const [state, setState] = useState(State.LOADING);
26
+ const user = useContext(UserContext);
27
+ const userId = user.user?.user.id;
28
29
useEffect(() => {
30
log.debug("Play page loaded.");
@@ -49,6 +53,13 @@ export default function Page() {
53
fetchData();
54
// eslint-disable-next-line react-hooks/exhaustive-deps
55
}, []);
56
+
57
+ // Sanity check
58
+ if (!audioFileId || !userId) {
59
+ log.error("Missing audio file ID or user ID");
60
+ return <div>Missing audio file ID</div>;
61
+ }
62
+
63
return (
64
<>
65
<div className="flex justify-between">
@@ -65,7 +76,7 @@ export default function Page() {
76
<div className="text-red-500">Failed to load song</div>
77
)}
78
{state === State.LOADED && (
68
- <div className="text-green-500">Song loaded</div>
79
+ <Player audioId={audioFileId} userId={userId} />
80
)}
81
</div>
82
</div>