mattricesound commited on
Commit
c2a47f9
·
1 Parent(s): 05426cb

Web real-time reverb, remove other controls

Browse files
web/package-lock.json CHANGED
@@ -8,6 +8,7 @@
8
  "name": "web",
9
  "version": "0.1.0",
10
  "dependencies": {
 
11
  "@types/node": "20.4.2",
12
  "@types/react": "18.2.15",
13
  "@types/react-dom": "18.2.7",
@@ -19,6 +20,7 @@
19
  "react": "18.2.0",
20
  "react-dom": "18.2.0",
21
  "tailwindcss": "3.3.3",
 
22
  "typescript": "5.1.6"
23
  }
24
  },
@@ -104,6 +106,14 @@
104
  "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
105
  }
106
  },
 
 
 
 
 
 
 
 
107
  "node_modules/@humanwhocodes/config-array": {
108
  "version": "0.11.10",
109
  "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
@@ -708,6 +718,18 @@
708
  "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
709
  "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag=="
710
  },
 
 
 
 
 
 
 
 
 
 
 
 
711
  "node_modules/autoprefixer": {
712
  "version": "10.4.14",
713
  "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
@@ -3611,6 +3633,16 @@
3611
  "node": ">=0.10.0"
3612
  }
3613
  },
 
 
 
 
 
 
 
 
 
 
3614
  "node_modules/streamsearch": {
3615
  "version": "1.1.0",
3616
  "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@@ -3909,6 +3941,15 @@
3909
  "node": ">=8.0"
3910
  }
3911
  },
 
 
 
 
 
 
 
 
 
3912
  "node_modules/ts-interface-checker": {
3913
  "version": "0.1.13",
3914
  "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
@@ -3926,9 +3967,9 @@
3926
  }
3927
  },
3928
  "node_modules/tslib": {
3929
- "version": "2.6.0",
3930
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
3931
- "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA=="
3932
  },
3933
  "node_modules/tsutils": {
3934
  "version": "3.21.0",
 
8
  "name": "web",
9
  "version": "0.1.0",
10
  "dependencies": {
11
+ "@heroicons/react": "^2.0.18",
12
  "@types/node": "20.4.2",
13
  "@types/react": "18.2.15",
14
  "@types/react-dom": "18.2.7",
 
20
  "react": "18.2.0",
21
  "react-dom": "18.2.0",
22
  "tailwindcss": "3.3.3",
23
+ "tone": "^14.7.77",
24
  "typescript": "5.1.6"
25
  }
26
  },
 
106
  "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
107
  }
108
  },
109
+ "node_modules/@heroicons/react": {
110
+ "version": "2.0.18",
111
+ "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz",
112
+ "integrity": "sha512-7TyMjRrZZMBPa+/5Y8lN0iyvUU/01PeMGX2+RE7cQWpEUIcb4QotzUObFkJDejj/HUH4qjP/eQ0gzzKs2f+6Yw==",
113
+ "peerDependencies": {
114
+ "react": ">= 16"
115
+ }
116
+ },
117
  "node_modules/@humanwhocodes/config-array": {
118
  "version": "0.11.10",
119
  "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
 
718
  "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
719
  "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag=="
720
  },
721
+ "node_modules/automation-events": {
722
+ "version": "6.0.8",
723
+ "resolved": "https://registry.npmjs.org/automation-events/-/automation-events-6.0.8.tgz",
724
+ "integrity": "sha512-OXI9rEbA0LwWr+Tmvka4EHtVHBIVw8KD2NM7fIGjd4dyGnuiM3ULZL+Jlo4aKXZDY98raT4R4rEDOHAbz8Jm9A==",
725
+ "dependencies": {
726
+ "@babel/runtime": "^7.22.6",
727
+ "tslib": "^2.6.1"
728
+ },
729
+ "engines": {
730
+ "node": ">=16.1.0"
731
+ }
732
+ },
733
  "node_modules/autoprefixer": {
734
  "version": "10.4.14",
735
  "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
 
3633
  "node": ">=0.10.0"
3634
  }
3635
  },
3636
+ "node_modules/standardized-audio-context": {
3637
+ "version": "25.3.54",
3638
+ "resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.54.tgz",
3639
+ "integrity": "sha512-1b5YTivCBc52MFlg3yUFdbfQZBb4wyFUjbvzBn6JD1FJM1nd3RTg3ddyGWRT8XVOC0KUwY2h4R9YrqLpQ04JPQ==",
3640
+ "dependencies": {
3641
+ "@babel/runtime": "^7.22.6",
3642
+ "automation-events": "^6.0.7",
3643
+ "tslib": "^2.6.0"
3644
+ }
3645
+ },
3646
  "node_modules/streamsearch": {
3647
  "version": "1.1.0",
3648
  "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
 
3941
  "node": ">=8.0"
3942
  }
3943
  },
3944
+ "node_modules/tone": {
3945
+ "version": "14.7.77",
3946
+ "resolved": "https://registry.npmjs.org/tone/-/tone-14.7.77.tgz",
3947
+ "integrity": "sha512-tCfK73IkLHyzoKUvGq47gyDyxiKLFvKiVCOobynGgBB9Dl0NkxTM2p+eRJXyCYrjJwy9Y0XCMqD3uOYsYt2Fdg==",
3948
+ "dependencies": {
3949
+ "standardized-audio-context": "^25.1.8",
3950
+ "tslib": "^2.0.1"
3951
+ }
3952
+ },
3953
  "node_modules/ts-interface-checker": {
3954
  "version": "0.1.13",
3955
  "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
 
3967
  }
3968
  },
3969
  "node_modules/tslib": {
3970
+ "version": "2.6.1",
3971
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
3972
+ "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig=="
3973
  },
3974
  "node_modules/tsutils": {
3975
  "version": "3.21.0",
web/package.json CHANGED
@@ -9,6 +9,7 @@
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
 
12
  "@types/node": "20.4.2",
13
  "@types/react": "18.2.15",
14
  "@types/react-dom": "18.2.7",
@@ -20,6 +21,7 @@
20
  "react": "18.2.0",
21
  "react-dom": "18.2.0",
22
  "tailwindcss": "3.3.3",
 
23
  "typescript": "5.1.6"
24
  }
25
  }
 
9
  "lint": "next lint"
10
  },
11
  "dependencies": {
12
+ "@heroicons/react": "^2.0.18",
13
  "@types/node": "20.4.2",
14
  "@types/react": "18.2.15",
15
  "@types/react-dom": "18.2.7",
 
21
  "react": "18.2.0",
22
  "react-dom": "18.2.0",
23
  "tailwindcss": "3.3.3",
24
+ "tone": "^14.7.77",
25
  "typescript": "5.1.6"
26
  }
27
  }
web/public/test-clips/skibidi-toilet.mp3 ADDED
Binary file (186 kB). View file
 
web/src/app/audio-clip.tsx ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as Tone from "tone";
2
+ import { useState, useRef, useEffect} from "react";
3
+ import { PlayCircleIcon, StopCircleIcon } from '@heroicons/react/24/solid'
4
+
5
+
6
+ const SAMPLE_RATE = 32000;
7
+ const REVERB_ROOM = 0.8;
8
+ const GAIN_AMOUNT = -10;
9
+ const LENGTH = 10;
10
+ function AudioClip(props: any) {
11
+ const [reverbAmount, setReverbAmount] = useState(50);
12
+ const [playing, setPlaying] = useState(false);
13
+ const [progress, setProgress] = useState(0);
14
+
15
+ const player = useRef<Tone.Player | null>(null);
16
+ const reverb = useRef<Tone.Freeverb | null>(null);
17
+ const gain = useRef<Tone.Gain<"decibels"> | null>(null);
18
+ // const interval = useRef<any>(null);
19
+
20
+
21
+ useEffect(() => {
22
+ player.current = new Tone.Player(props.src.filename)
23
+ reverb.current = new Tone.Freeverb(REVERB_ROOM);
24
+ gain.current = new Tone.Gain(GAIN_AMOUNT, "decibels");
25
+
26
+ player.current.connect(reverb.current);
27
+ player.current.volume.value = -6;
28
+ reverb.current.connect(gain.current);
29
+ gain.current.toDestination();
30
+
31
+ player.current.loop = true;
32
+ player.current.setLoopPoints(0, 10);
33
+
34
+
35
+ }, []);
36
+
37
+
38
+ useEffect(() => {
39
+ const interval = setInterval(() => {
40
+ if (player.current) {
41
+ if (progress >= 100 || !playing) {
42
+ setProgress(0);
43
+ } else {
44
+ setProgress(progress + 1);
45
+ }
46
+ }
47
+ }, 90);
48
+ return () => clearInterval(interval);
49
+ }, [playing, progress]);
50
+
51
+
52
+
53
+ const handleClick = async () => {
54
+ await Tone.start();
55
+ if (player.current && player.current.state === "started") {
56
+ player.current.stop();
57
+ setPlaying(false);
58
+
59
+
60
+ } else if (player.current && player.current.state === "stopped") {
61
+ player.current.start();
62
+ setPlaying(true);
63
+
64
+ } else {
65
+ console.log("player not ready");
66
+ setPlaying(false);
67
+ }
68
+
69
+ };
70
+
71
+
72
+
73
+ const handleDownload = async () => {
74
+ await Tone.start();
75
+ await Tone.Offline(
76
+ () => {
77
+ const offlinePlayer = new Tone.Player(props.src)
78
+ const offlineReverb = new Tone.Freeverb(REVERB_ROOM)
79
+ const offlineGain = new Tone.Gain(GAIN_AMOUNT, "decibels").toDestination();
80
+ offlinePlayer.connect(offlineReverb);
81
+ offlineReverb.connect(offlineGain);
82
+ Tone.Transport.start();
83
+ Tone.Transport.scheduleOnce(() => {
84
+ Tone.Transport.stop();
85
+ }, offlinePlayer.buffer.duration);
86
+ },
87
+ LENGTH,
88
+ 1,
89
+ SAMPLE_RATE
90
+ ).then((renderedBuffer) => {
91
+ const url = URL.createObjectURL(
92
+ new Blob([renderedBuffer.getChannelData(0)], { type: "audio/wav" })
93
+ );
94
+ const downloadLink = document.createElement("a");
95
+ downloadLink.href = url;
96
+ downloadLink.download = "processed_audio.wav";
97
+ downloadLink.click();
98
+ });
99
+ };
100
+
101
+ const handleReverbChange = (event: any) => {
102
+ setReverbAmount(event.target.value);
103
+ if (reverb.current)
104
+ reverb.current.wet.value = parseInt(event.target.value) / 100;
105
+ }
106
+ let width = `w-[${progress}%]`;
107
+ return (
108
+ <div className="flex flex-col justify-center p-5" >
109
+ <div className="flex items-center">
110
+ {playing ?
111
+ <StopCircleIcon className="h-6 w-6 text-blue-500 cursor-pointer mr-2 hover:text-blue-600" onClick={handleClick}/> :
112
+ <PlayCircleIcon className="h-6 w-6 text-blue-500 cursor-pointer mr-2 hover:text-blue-600" onClick={handleClick}/>
113
+ }
114
+ <progress value={progress} max="100"/>
115
+ </div>
116
+ <div className="flex justify-around text-gray-500 my-2">
117
+ <div>{props.src.key}</div>
118
+ <div>{props.src.bpm}</div>
119
+ </div>
120
+ <div className="flex flex-col justify-center my-2">
121
+ <label className="flex justify-center my-2">Reverb</label>
122
+ <input className="cursor-pointer" type="range" value={reverbAmount} onChange={handleReverbChange} min={0} max={100}/>
123
+ </div>
124
+ <button
125
+ className="flex items-center justify-center border-2 border-black rounded-lg px-10 py-1 hover:bg-black hover:text-white my-2 dark:bg-gray-800 dark:text-white dark:hover:bg-white dark:hover:text-black"
126
+ onClick={handleDownload}
127
+ >
128
+ Download
129
+ </button>
130
+ </div>
131
+ );
132
+ }
133
+ export default AudioClip;
web/src/app/globals.css CHANGED
@@ -18,10 +18,21 @@
18
 
19
  body {
20
  color: rgb(var(--foreground-rgb));
21
- background: linear-gradient(
22
- to bottom,
23
  transparent,
24
- rgb(var(--background-end-rgb))
25
- )
26
- rgb(var(--background-start-rgb));
27
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  body {
20
  color: rgb(var(--foreground-rgb));
21
+ background: linear-gradient(to bottom,
 
22
  transparent,
23
+ rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb));
 
 
24
  }
25
+
26
+ progress[value]::-webkit-progress-value {
27
+ background-image:
28
+ -webkit-linear-gradient(-45deg,
29
+ transparent 33%, rgba(0, 0, 0, .1) 33%,
30
+ rgba(0, 0, 0, .1) 66%, transparent 66%),
31
+ -webkit-linear-gradient(top,
32
+ rgba(255, 255, 255, .25),
33
+ rgba(0, 0, 0, .25)),
34
+ -webkit-linear-gradient(left, #09c, #f44);
35
+
36
+ border-radius: 2px;
37
+ background-size: 35px 20px, 100% 100%, 100% 100%;
38
+ }
web/src/app/page.tsx CHANGED
@@ -1,22 +1,13 @@
1
  "use client";
2
 
3
- import Image from "next/image";
4
  import { useState } from "react";
 
5
 
6
  export default function Home() {
7
- const [length, setLength] = useState(10);
8
- const [BPM, setBPM] = useState(120);
9
- const [style, setStyle] = useState("");
10
  const [loading, setLoading] = useState(false);
11
  const [finished, setFinished] = useState(false);
12
- const [clips, setClips] = useState<string[]>([]);
13
-
14
- function onLengthChange(e: React.ChangeEvent<HTMLInputElement>) {
15
- setLength(Number(e.target.value));
16
- }
17
- function onBPMChange(e: React.ChangeEvent<HTMLInputElement>) {
18
- setBPM(Number(e.target.value));
19
- }
20
 
21
  function handleStyleClick(style: string) {
22
  setStyle(style);
@@ -34,7 +25,7 @@ export default function Home() {
34
  setFinished(true);
35
  setLoading(false);
36
  setClips(testClips);
37
- }, 3000);
38
  }
39
 
40
  function downloadFile(url: string) {
@@ -64,7 +55,7 @@ export default function Home() {
64
  <main className="flex min-h-screen flex-col items-center">
65
  <h1 className="text-6xl font-bold text-center mt-10">SoundSauce</h1>
66
  <div className="flex flex-col items-center justify-center">
67
- Your unique flavor
68
  </div>
69
  <div className="flex flex-col items-center justify-center m-20 w-full">
70
  <div>Select a style</div>
@@ -72,11 +63,11 @@ export default function Home() {
72
  {styles.map((s) => {
73
  let background = "bg-inherit";
74
  if (s === style) {
75
- background = "bg-white";
76
  }
77
  return (
78
  <button
79
- className={`flex items-center justify-center border-2 border-white rounded-lg px-4 py-1 hover:bg-white ${background}`}
80
  onClick={() => handleStyleClick(s)}
81
  key={s}
82
  >
@@ -85,32 +76,6 @@ export default function Home() {
85
  );
86
  })}
87
  </div>
88
- <div className="flex justify-around w-1/3 m-5">
89
- <div className="flex flex-col items-center justify-center">
90
- <div>Length</div>
91
- <input
92
- type="range"
93
- min={1}
94
- max={20}
95
- step={1}
96
- value={length}
97
- onChange={onLengthChange}
98
- />
99
- <div>{length} seconds</div>
100
- </div>
101
- <div className="flex flex-col items-center justify-center">
102
- <div>BPM</div>
103
- <input
104
- type="range"
105
- min={60}
106
- max={180}
107
- step={1}
108
- value={BPM}
109
- onChange={onBPMChange}
110
- />
111
- <div>{BPM}</div>
112
- </div>
113
- </div>
114
  <button
115
  className="flex items-center justify-center border-2 border-green-500 rounded-lg px-10 py-1 hover:bg-green-500"
116
  onClick={handleGenerationStart}
@@ -130,20 +95,6 @@ export default function Home() {
130
  );
131
  }
132
 
133
- function AudioClip(props: any) {
134
- return (
135
- <div className="flex flex-col justify-center p-5">
136
- <audio controls src={props.src} />
137
- <button
138
- className="flex items-center justify-center border-2 border-black rounded-lg px-10 py-1 mt-2 hover:bg-black hover:text-white"
139
- onClick={() => props.downloadFile(props.src)}
140
- >
141
- Download
142
- </button>
143
- </div>
144
- );
145
- }
146
-
147
  let styles = [
148
  "Travis Scott",
149
  "Drake",
@@ -154,7 +105,22 @@ let styles = [
154
  ];
155
 
156
  let testClips = [
157
- "./test-clips/chipmunk.wav",
158
- "./test-clips/chipmunk.wav",
159
- "./test-clips/chipmunk.wav",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  ];
 
1
  "use client";
2
 
 
3
  import { useState } from "react";
4
+ import AudioClip from "./audio-clip";
5
 
6
  export default function Home() {
7
+ const [style, setStyle] = useState("d");
 
 
8
  const [loading, setLoading] = useState(false);
9
  const [finished, setFinished] = useState(false);
10
+ const [clips, setClips] = useState<Object[]>([]);
 
 
 
 
 
 
 
11
 
12
  function handleStyleClick(style: string) {
13
  setStyle(style);
 
25
  setFinished(true);
26
  setLoading(false);
27
  setClips(testClips);
28
+ }, 1);
29
  }
30
 
31
  function downloadFile(url: string) {
 
55
  <main className="flex min-h-screen flex-col items-center">
56
  <h1 className="text-6xl font-bold text-center mt-10">SoundSauce</h1>
57
  <div className="flex flex-col items-center justify-center">
58
+ Your unique melody flavor
59
  </div>
60
  <div className="flex flex-col items-center justify-center m-20 w-full">
61
  <div>Select a style</div>
 
63
  {styles.map((s) => {
64
  let background = "bg-inherit";
65
  if (s === style) {
66
+ background = "bg-white dark:text-black";
67
  }
68
  return (
69
  <button
70
+ className={`flex items-center justify-center border-2 border-white rounded-lg px-4 py-1 dark:hover:text-black hover:bg-white ${background}`}
71
  onClick={() => handleStyleClick(s)}
72
  key={s}
73
  >
 
76
  );
77
  })}
78
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  <button
80
  className="flex items-center justify-center border-2 border-green-500 rounded-lg px-10 py-1 hover:bg-green-500"
81
  onClick={handleGenerationStart}
 
95
  );
96
  }
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  let styles = [
99
  "Travis Scott",
100
  "Drake",
 
105
  ];
106
 
107
  let testClips = [
108
+ {
109
+ filename: "./test-clips/chipmunk.wav",
110
+ key: "G Maj",
111
+ bpm: 120,
112
+ length: 10,
113
+ },
114
+ {
115
+ filename: "./test-clips/skibidi-toilet.mp3",
116
+ key: "A Min",
117
+ bpm: 144,
118
+ length: 11.5,
119
+ },
120
+ {
121
+ filename: "./test-clips/chipmunk.wav",
122
+ key: "G Maj",
123
+ bpm: 120,
124
+ length: 10
125
+ },
126
  ];