[READ-ONLY] Mirror of https://github.com/flo-bit/svelte-audio-visualizations. a few different simple audio visualizations (esp for voice input/output) for svelte flo-bit.dev/svelte-audio-visualizations/
0

Configure Feed

Select the types of activity you want to include in your feed.

fix some bugs

Florian (Oct 16, 2024, 2:33 AM +0200) b74e5d05 0cad2ffc

+89 -43
+17 -2
src/lib/visualizations/audio/AudioFrequency.svelte
··· 1 1 <script lang="ts"> 2 - import type { WavRecorder, AudioFilePlayer, WavStreamPlayer } from '$lib/visualizations/wavtools'; 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 + export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency'; 7 + export let minDecibels: number = -100; 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 - raf(() => (values = audio ? audio.getFrequencies('music').values : new Float32Array([0]))); 13 + raf(() => { 14 + if(!audio) { 15 + values = new Float32Array([0]); 16 + return; 17 + } 18 + 19 + if(audio instanceof WavRecorder && !audio.recording) { 20 + values = new Float32Array([0]); 21 + return; 22 + } 23 + 24 + values = audio.getFrequencies(analysisType, minDecibels, maxDecibels).values; 25 + }); 11 26 12 27 //Recreate the getValues function when values changes (to trigger reactivity) 13 28 $: getValues = (detail: number) => normalizeArray(values, detail);
+4 -3
src/lib/visualizations/audio/BarAudioVisualizer.svelte
··· 6 6 7 7 export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null; 8 8 export let glow: number | undefined = 3; 9 - export let detail: number | undefined = 50; 9 + export let detail: number | undefined = 20; 10 + export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency'; 10 11 </script> 11 12 12 - <AudioFrequency {audio} let:getValues> 13 + <AudioFrequency {audio} {analysisType} let:getValues> 13 14 <Glow {glow}> 14 - <BarVisualizer values={getValues(detail || 50)} {...$$restProps} /> 15 + <BarVisualizer values={getValues(detail || 20)} {...$$restProps} /> 15 16 </Glow> 16 17 </AudioFrequency>
+2 -1
src/lib/visualizations/audio/CircleBarAudioVisualizer.svelte
··· 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 + export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency'; 10 11 </script> 11 12 12 - <AudioFrequency {audio} let:getValues> 13 + <AudioFrequency {audio} {analysisType} let:getValues> 13 14 <Glow {glow}> 14 15 <CircleBarVisualizer values={getValues(detail || 50)} {...$$restProps} /> 15 16 </Glow>
+2 -1
src/lib/visualizations/audio/CircleCirclesAudioVisualizer.svelte
··· 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 + export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency'; 10 11 </script> 11 12 12 - <AudioFrequency {audio} let:getValues> 13 + <AudioFrequency {audio} {analysisType} let:getValues> 13 14 <Glow {glow}> 14 15 <CircleCirclesVisualizer values={getValues(detail || 50)} {...$$restProps} /> 15 16 </Glow>
+4 -3
src/lib/visualizations/audio/DeformedCircleAudioVisualizer.svelte
··· 6 6 7 7 export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null; 8 8 export let glow: number | undefined = 20; 9 - export let detail: number | undefined = 50; 9 + export let detail: number | undefined = 20; 10 + export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency'; 10 11 </script> 11 12 12 - <AudioFrequency {audio} let:getValues> 13 + <AudioFrequency {audio} {analysisType} let:getValues> 13 14 <Glow {glow}> 14 - <DeformedCircleVisualizer values={getValues(detail || 50)} {...$$restProps} /> 15 + <DeformedCircleVisualizer values={getValues(detail || 20)} {...$$restProps} /> 15 16 </Glow> 16 17 </AudioFrequency>
+2 -1
src/lib/visualizations/audio/InnerGlowAudioVisualizer.svelte
··· 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 + export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency'; 10 11 </script> 11 12 12 - <AudioFrequency {audio} let:getValues> 13 + <AudioFrequency {audio} {analysisType} let:getValues> 13 14 <Glow {glow}> 14 15 <InnerGlowVisualizer values={getValues(detail || 50)} {...$$restProps} /> 15 16 </Glow>
+2 -1
src/lib/visualizations/audio/MicrophoneAudioVisualizer.svelte
··· 6 6 7 7 export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null; 8 8 export let glow: number | undefined = 3; 9 + export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency'; 9 10 </script> 10 11 11 - <AudioFrequency {audio} let:getValues> 12 + <AudioFrequency {audio} {analysisType} let:getValues> 12 13 <Glow {glow}> 13 14 <MicrophoneVisualizer value={getValues(1)[0]} {...$$restProps} /> 14 15 </Glow>
+2 -1
src/lib/visualizations/audio/SpeakerAudioVisualizer.svelte
··· 6 6 7 7 export let audio: AudioFilePlayer | WavRecorder | WavStreamPlayer | null; 8 8 export let glow: number | undefined = 3; 9 + export let analysisType: 'music' | 'voice' | 'frequency' = 'frequency'; 9 10 </script> 10 11 11 - <AudioFrequency {audio} let:getValues> 12 + <AudioFrequency {audio} {analysisType} let:getValues> 12 13 <Glow {glow}> 13 14 <SpeakerVisualizer value={getValues(1)[0]} {...$$restProps} /> 14 15 </Glow>
+7 -2
src/lib/visualizations/core/utils.ts
··· 6 6 let mounted = true; 7 7 8 8 async function render() { 9 - if (!mounted) return; 10 - callback(); 9 + if (!mounted || !callback) return; 10 + 11 + try { 12 + callback(); 13 + } catch (e) { 14 + console.error(e); 15 + } 11 16 requestAnimationFrame(render); 12 17 } 13 18
+47 -28
src/routes/+page.svelte
··· 11 11 import BarAudioVisualizer from '$lib/visualizations/audio/BarAudioVisualizer.svelte'; 12 12 import CircleCirclesAudioVisualizer from '$lib/visualizations/audio/CircleCirclesAudioVisualizer.svelte'; 13 13 14 - let audio: WavRecorder | AudioFilePlayer | null = null; 14 + 15 + export let wavRecorder: WavRecorder = new WavRecorder({ sampleRate: 24000 }); 16 + 17 + export let player = new AudioFilePlayer(); 18 + 19 + export let currentlyPlaying: WavRecorder | AudioFilePlayer | null = null; 20 + 15 21 let state: 'recording' | 'music' | null = null; 16 22 17 - //Stop playing music, if wrong state 18 - $: if (state !== 'music' && audio instanceof WavRecorder) { 19 - audio.end(); 20 - audio = null; 23 + let analysisType: 'music' | 'voice' = 'voice'; 24 + 25 + $: analysisType = state === 'music' ? 'music' : 'voice'; 26 + 27 + async function microphone() { 28 + if (state === 'recording') { 29 + wavRecorder.end(); 30 + state = null; 31 + return; 32 + } 33 + player.stop(); 34 + 35 + await wavRecorder.begin(); 36 + 37 + wavRecorder.record(); 38 + 39 + currentlyPlaying = wavRecorder; 40 + 41 + state = 'recording'; 21 42 } 22 43 23 - //Stop recording, if wrong state 24 - $: if (state !== 'recording' && audio instanceof AudioFilePlayer) { 25 - audio.stop(); 26 - audio = null; 27 - } 44 + async function music() { 45 + if (state === 'music') { 46 + player.stop(); 47 + state = null; 48 + return; 49 + } 50 + if (wavRecorder.recording) wavRecorder.end(); 28 51 29 - //Start playing music, if not playing already 30 - $: if (state == 'music' && !audio) { 31 - let player = (audio = new AudioFilePlayer()); 32 - audio.loadFile('/svelte-audio-visualizations/music.mp3').then(() => player.play()); 33 - } 52 + await player.loadFile('/svelte-audio-visualizations/music.mp3'); 53 + 54 + player.play(); 55 + currentlyPlaying = player; 34 56 35 - //Start recording, if not recording already 36 - $: if (state == 'recording' && !audio) { 37 - let recorder = (audio = new WavRecorder({ sampleRate: 24000 })); 38 - audio.begin().then(() => recorder.record()); 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 - on:click={() => (state = state == 'recording' ? null : 'recording')} 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 - on:click={() => (state = state == 'music' ? null : 'music')} 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 - <CircleBarAudioVisualizer {audio} startHue={0} endHue={50} rotate={2} /> 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 - <BarAudioVisualizer {audio} barSpacing={8} startHue={0} endHue={50} center /> 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 - <CircleCirclesAudioVisualizer {audio} startHue={0} endHue={50} /> 97 + <CircleCirclesAudioVisualizer audio={currentlyPlaying} startHue={0} endHue={50} {analysisType} /> 79 98 </div> 80 99 81 - <AudioFrequency {audio} let:getValues> 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 - <DeformedCircleVisualizer values={getValues(16)} startHue={0} endHue={50} /> 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 - <MicrophoneVisualizer value={getValues(1)[0]} /> 115 + <MicrophoneVisualizer value={getValues(3)[1]} /> 97 116 </div> 98 117 <div class="size-20"> 99 - <SpeakerVisualizer value={getValues(1)[0]} /> 118 + <SpeakerVisualizer value={getValues(3)[1]} /> 100 119 </div> 101 120 </div> 102 121 </AudioFrequency>