···
1
1
<script lang="ts">
2
2
-
import type { WavRecorder, AudioFilePlayer, WavStreamPlayer } from '$lib/visualizations/wavtools';
2
2
+
import { WavRecorder, AudioFilePlayer, WavStreamPlayer } from '$lib/visualizations/wavtools';
3
3
import { raf, normalizeArray } from '../core/utils';
4
4
5
5
export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null;
6
6
+
export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency';
7
7
+
export let minDecibels: number = -100;
8
8
+
export let maxDecibels: number = -30;
6
9
7
10
let values: Float32Array = new Float32Array([0]);
8
11
9
12
//Update frequency values every render frame
10
10
-
raf(() => (values = audio ? audio.getFrequencies('music').values : new Float32Array([0])));
13
13
+
raf(() => {
14
14
+
if(!audio) {
15
15
+
values = new Float32Array([0]);
16
16
+
return;
17
17
+
}
18
18
+
19
19
+
if(audio instanceof WavRecorder && !audio.recording) {
20
20
+
values = new Float32Array([0]);
21
21
+
return;
22
22
+
}
23
23
+
24
24
+
values = audio.getFrequencies(analysisType, minDecibels, maxDecibels).values;
25
25
+
});
11
26
12
27
//Recreate the getValues function when values changes (to trigger reactivity)
13
28
$: getValues = (detail: number) => normalizeArray(values, detail);
···
6
6
7
7
export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null;
8
8
export let glow: number | undefined = 3;
9
9
-
export let detail: number | undefined = 50;
9
9
+
export let detail: number | undefined = 20;
10
10
+
export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency';
10
11
</script>
11
12
12
12
-
<AudioFrequency {audio} let:getValues>
13
13
+
<AudioFrequency {audio} {analysisType} let:getValues>
13
14
<Glow {glow}>
14
14
-
<BarVisualizer values={getValues(detail || 50)} {...$$restProps} />
15
15
+
<BarVisualizer values={getValues(detail || 20)} {...$$restProps} />
15
16
</Glow>
16
17
</AudioFrequency>
···
7
7
export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null;
8
8
export let glow: number | undefined = 3;
9
9
export let detail: number | undefined = 50;
10
10
+
export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency';
10
11
</script>
11
12
12
12
-
<AudioFrequency {audio} let:getValues>
13
13
+
<AudioFrequency {audio} {analysisType} let:getValues>
13
14
<Glow {glow}>
14
15
<CircleBarVisualizer values={getValues(detail || 50)} {...$$restProps} />
15
16
</Glow>
···
7
7
export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null;
8
8
export let glow: number | undefined = 3;
9
9
export let detail: number | undefined = 50;
10
10
+
export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency';
10
11
</script>
11
12
12
12
-
<AudioFrequency {audio} let:getValues>
13
13
+
<AudioFrequency {audio} {analysisType} let:getValues>
13
14
<Glow {glow}>
14
15
<CircleCirclesVisualizer values={getValues(detail || 50)} {...$$restProps} />
15
16
</Glow>
···
6
6
7
7
export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null;
8
8
export let glow: number | undefined = 20;
9
9
-
export let detail: number | undefined = 50;
9
9
+
export let detail: number | undefined = 20;
10
10
+
export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency';
10
11
</script>
11
12
12
12
-
<AudioFrequency {audio} let:getValues>
13
13
+
<AudioFrequency {audio} {analysisType} let:getValues>
13
14
<Glow {glow}>
14
14
-
<DeformedCircleVisualizer values={getValues(detail || 50)} {...$$restProps} />
15
15
+
<DeformedCircleVisualizer values={getValues(detail || 20)} {...$$restProps} />
15
16
</Glow>
16
17
</AudioFrequency>
···
7
7
export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null;
8
8
export let glow: number | undefined = 10;
9
9
export let detail: number | undefined = 50;
10
10
+
export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency';
10
11
</script>
11
12
12
12
-
<AudioFrequency {audio} let:getValues>
13
13
+
<AudioFrequency {audio} {analysisType} let:getValues>
13
14
<Glow {glow}>
14
15
<InnerGlowVisualizer values={getValues(detail || 50)} {...$$restProps} />
15
16
</Glow>
···
6
6
7
7
export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null;
8
8
export let glow: number | undefined = 3;
9
9
+
export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency';
9
10
</script>
10
11
11
11
-
<AudioFrequency {audio} let:getValues>
12
12
+
<AudioFrequency {audio} {analysisType} let:getValues>
12
13
<Glow {glow}>
13
14
<MicrophoneVisualizer value={getValues(1)[0]} {...$$restProps} />
14
15
</Glow>
···
6
6
7
7
export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null;
8
8
export let glow: number | undefined = 3;
9
9
+
export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency';
9
10
</script>
10
11
11
11
-
<AudioFrequency {audio} let:getValues>
12
12
+
<AudioFrequency {audio} {analysisType} let:getValues>
12
13
<Glow {glow}>
13
14
<SpeakerVisualizer value={getValues(1)[0]} {...$$restProps} />
14
15
</Glow>
···
6
6
let mounted = true;
7
7
8
8
async function render() {
9
9
-
if (!mounted) return;
10
10
-
callback();
9
9
+
if (!mounted || !callback) return;
10
10
+
11
11
+
try {
12
12
+
callback();
13
13
+
} catch (e) {
14
14
+
console.error(e);
15
15
+
}
11
16
requestAnimationFrame(render);
12
17
}
13
18
···
11
11
import BarAudioVisualizer from '$lib/visualizations/audio/BarAudioVisualizer.svelte';
12
12
import CircleCirclesAudioVisualizer from '$lib/visualizations/audio/CircleCirclesAudioVisualizer.svelte';
13
13
14
14
-
let audio: WavRecorder | AudioFilePlayer | null = null;
14
14
+
15
15
+
export let wavRecorder: WavRecorder = new WavRecorder({ sampleRate: 24000 });
16
16
+
17
17
+
export let player = new AudioFilePlayer();
18
18
+
19
19
+
export let currentlyPlaying: WavRecorder | AudioFilePlayer | null = null;
20
20
+
15
21
let state: 'recording' | 'music' | null = null;
16
22
17
17
-
//Stop playing music, if wrong state
18
18
-
$: if (state !== 'music' && audio instanceof WavRecorder) {
19
19
-
audio.end();
20
20
-
audio = null;
23
23
+
let analysisType: 'music' | 'voice' = 'voice';
24
24
+
25
25
+
$: analysisType = state === 'music' ? 'music' : 'voice';
26
26
+
27
27
+
async function microphone() {
28
28
+
if (state === 'recording') {
29
29
+
wavRecorder.end();
30
30
+
state = null;
31
31
+
return;
32
32
+
}
33
33
+
player.stop();
34
34
+
35
35
+
await wavRecorder.begin();
36
36
+
37
37
+
wavRecorder.record();
38
38
+
39
39
+
currentlyPlaying = wavRecorder;
40
40
+
41
41
+
state = 'recording';
21
42
}
22
43
23
23
-
//Stop recording, if wrong state
24
24
-
$: if (state !== 'recording' && audio instanceof AudioFilePlayer) {
25
25
-
audio.stop();
26
26
-
audio = null;
27
27
-
}
44
44
+
async function music() {
45
45
+
if (state === 'music') {
46
46
+
player.stop();
47
47
+
state = null;
48
48
+
return;
49
49
+
}
50
50
+
if (wavRecorder.recording) wavRecorder.end();
28
51
29
29
-
//Start playing music, if not playing already
30
30
-
$: if (state == 'music' && !audio) {
31
31
-
let player = (audio = new AudioFilePlayer());
32
32
-
audio.loadFile('/svelte-audio-visualizations/music.mp3').then(() => player.play());
33
33
-
}
52
52
+
await player.loadFile('/svelte-audio-visualizations/music.mp3');
53
53
+
54
54
+
player.play();
55
55
+
currentlyPlaying = player;
34
56
35
35
-
//Start recording, if not recording already
36
36
-
$: if (state == 'recording' && !audio) {
37
37
-
let recorder = (audio = new WavRecorder({ sampleRate: 24000 }));
38
38
-
audio.begin().then(() => recorder.record());
57
57
+
state = 'music';
39
58
}
40
59
</script>
41
60
···
44
63
<div class="flex gap-2 flex-col sm:flex-row">
45
64
<button
46
65
type="button"
47
47
-
on:click={() => (state = state == 'recording' ? null : 'recording')}
66
66
+
on:click={microphone}
48
67
class="rounded-full px-3 py-2 text-sm font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500 {state ===
49
68
'recording'
50
69
? 'text-stone-500 bg-stone-500/10 border border-stone-500/20 hover:bg-stone-500/20'
···
53
72
>
54
73
<button
55
74
type="button"
56
56
-
on:click={() => (state = state == 'music' ? null : 'music')}
75
75
+
on:click={music}
57
76
class="rounded-full px-3 py-2 text-sm font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500 {state ===
58
77
'music'
59
78
? 'text-stone-500 bg-stone-500/10 border border-stone-500/20 hover:bg-stone-500/20'
···
68
87
69
88
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-8">
70
89
<div class="h-64 w-full rounded-xl border border-white/15 p-4">
71
71
-
<CircleBarAudioVisualizer {audio} startHue={0} endHue={50} rotate={2} />
90
90
+
<CircleBarAudioVisualizer audio={currentlyPlaying} startHue={0} endHue={50} rotate={2} {analysisType} />
72
91
</div>
73
92
<div class="h-64 w-full rounded-xl border border-white/15 p-4">
74
74
-
<BarAudioVisualizer {audio} barSpacing={8} startHue={0} endHue={50} center />
93
93
+
<BarAudioVisualizer audio={currentlyPlaying} barSpacing={8} startHue={0} endHue={50} center {analysisType} />
75
94
</div>
76
95
77
96
<div class="h-64 w-full rounded-xl border border-white/15 p-4">
78
78
-
<CircleCirclesAudioVisualizer {audio} startHue={0} endHue={50} />
97
97
+
<CircleCirclesAudioVisualizer audio={currentlyPlaying} startHue={0} endHue={50} {analysisType} />
79
98
</div>
80
99
81
81
-
<AudioFrequency {audio} let:getValues>
100
100
+
<AudioFrequency audio={currentlyPlaying} let:getValues {analysisType}>
82
101
<div class="h-64 w-full rounded-xl border border-white/15 p-4">
83
102
<Glow glow={20}>
84
84
-
<DeformedCircleVisualizer values={getValues(16)} startHue={0} endHue={50} />
103
103
+
<DeformedCircleVisualizer values={getValues(8)} startHue={0} endHue={50} />
85
104
</Glow>
86
105
</div>
87
106
<div class="h-64 w-full rounded-xl border border-white/15 overflow-hidden">
···
93
112
class="h-64 w-full rounded-xl border border-white/15 overflow-hidden flex items-center justify-center gap-4"
94
113
>
95
114
<div class="size-20">
96
96
-
<MicrophoneVisualizer value={getValues(1)[0]} />
115
115
+
<MicrophoneVisualizer value={getValues(3)[1]} />
97
116
</div>
98
117
<div class="size-20">
99
99
-
<SpeakerVisualizer value={getValues(1)[0]} />
118
118
+
<SpeakerVisualizer value={getValues(3)[1]} />
100
119
</div>
101
120
</div>
102
121
</AudioFrequency>