Wizualizacje i sztuka generyczna stały się ostatnio obiektem mojego zainteresowania, dlatego postanowiłem spróbować swoich własnych sił na tym polu.
Efektem tego jest zbudowanie wizualizera muzyki, przedstawiającego dynamicznie zmiany w sile sygnału na różnych częstotliwościach. Wszystko działa w środowisku przeglądarki internetowej, bazując na javascripcie, Canvasie, Web Audio API.
Całość funkcjonuje następująco:
dane o poziomie i zakresie częstotliwości są najpierw pobierane z ścieżki dźwiękowej przy użyciu obiektów z Web Audio API, a potem w czasie rzeczywistym wyświetlane są na Canvasie.
Obróbka dźwięku
Żeby pobrać dane z utworu muzycznego potrzebne są 3 obiekty:
let audioContext = new AudioContext();
Niezbędny do tworzenia innych obiektów z tego API.
let analyser = audioContext.createAnalyser();
Używany do pobrania danych o zakresie i poziomie częstotliwości.
let source = audioContext.createMediaElementSource(/* Jakiś znacznik audio */);
Pozwala wykorzystywać API dane pobierane z elementów audio
Po ich stworzeniu należy ustanowić odpowiednie relacje między nimi, żeby można było później korzystać z informacji Audio, które będą przez nie przesyłane:
source.connect(analyser);
source.connect(audioContext.destination);
Żeby pobrać dane z analysera potrzebne jest wcześniejsze stworzenie tablicy, która zostanie wypełniona danymi:
let data = new Uint8Array(analyser.frequencyBinCount);
frequencyBinCount to informacja na ile częstotliwości zostanie podzielony sygnał
analyser.getByteFrequencyData(data);
Tablica jest wypełniana danymi o wartościach kanałów na poszczególnych częstotliwościach
Wizualizacja
Żeby wyświetlić uzyskane w ten sposób dane potrzebny jest znacznik canvas i jego context umożliwiający dynamiczne rysowanie na tym elemencie strony.
let context = document.querySelector(/* identyfikator canvasa */).getContext('2d');
Żeby wyświetlić dane pozyskane z Web Audio API wykorzystuje się kolejne instrukcje
context.clearRect(0,0,context.canvas.width,context.canvas.height);
Wymazuje dotychczasową zawartość canvasa
for(let i = 0 ; i < data.length ; i++){
let x = i * (context.canvas.width / data.length);
let y = context.canvas.height - ((context.canvas.height) * (data[i] / 255));
let width = (context.canvas.width / data.length);
let height = (data[i] / 255) * context.canvas.height;
this.context.fillRect(x,y,width,height);
}
Rysuje kolejne słupki dla wartości pobranych z Web Audio API
Żeby umożliwić płynną animacje instrukcje pozyskujące dane i rysujące należy umieścić wewnątrz funkcji podanej jako argument do requestAnimationFrame(), zagwarantuje to płynne generowanie kolejnych kadrów
requestAnimationFrame(loop);
Działający przykład:
Plik HTML:
<html>
<head>
<script src="visualizer.js" defer></script>
<link rel="Stylesheet" href="visualizer.css">
</head>
<body>
<button>Start</button>
<audio>
<source src="music.mp3">
</audio>
<canvas>
</body>
</html>
Plik Visualizer.js:
let Audio_API,Visual;
function start(){
function get_audio_API_control(){ /* OBRÓBKA DŹWIĘKU */
let audioContext = new AudioContext();
let analyser = audioContext.createAnalyser();
let source = audioContext.createMediaElementSource(document.querySelector('audio'));
source.connect(analyser);
source.connect(audioContext.destination);
return {
audioContext:audioContext,
analyser:analyser,
source:source,
data_array: new Uint8Array(analyser.frequencyBinCount),
get_frequency:function(){ /* Zwraca dane o sile sygnału na częstot. */
this.analyser.getByteFrequencyData(this.data_array);
return this.data_array;
}
};
}
if(typeof(Audio_API) == 'undefined') Audio_API = get_audio_API_control();
function get_canvas_API_control(Audio_API){ /* WIZUALIZACJA */
let context = document.querySelector('canvas').getContext('2d');
return{
context:context,
audio:Audio_API,
draw:function(){
let data = this.audio.get_frequency();
this.context.clearRect(0,0,this.context.canvas.width,this.context.canvas.height);
for(let i = 0 ; i < data.length ; i++){
let x = i * (this.context.canvas.width / data.length);
let y = this.context.canvas.height - ((this.context.canvas.height) * (data[i] / 255));
let width = this.context.canvas.width / data.length;
let height = (data[i] / 255) * this.context.canvas.height;
this.context.fillRect(x,y,width,height);
}
}
}
}
if(typeof(Visual) == 'undefined') Visual = get_canvas_API_control(Audio_API);
document.querySelector('audio').play();
function loop(){
Visual.draw();
requestAnimationFrame(loop);
}
loop();
}
document.body.querySelector('button').addEventListener('click',()=>{start()});
Z działania całości jestem zadowolony chociaż trzeba by było jeszcze sporo od strony graficznej poprawić.
CSS dołączam w ramach ciekawostki:
body{
margin:0;
}
canvas{
width:100vw;
height:100vw;
position:fixed;
top:0;
left:0;
z-index:1;
background-color:darkcyan;
}
button{
display:block;
position:fixed;
z-index:2;
top:5vw;
left:50vw;
transform:translateX(-50%);
padding:1%;
border-radius: 20%;
font-weight: bold;
background-color: transparent;
border-color: white;
color: white;
cursor: pointer;
}
Cały projekt wygląda u mnie tak:
Congratulations @kraken14! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s) :
You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word
STOP
Do not miss the last post from @hivebuzz: