Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Saeed-Khazaei/a3c697008d225ed738f12992b46c107d to your computer and use it in GitHub Desktop.
Save Saeed-Khazaei/a3c697008d225ed738f12992b46c107d to your computer and use it in GitHub Desktop.
Audio Waveform Visualizer
.wrapper
input#fileinput(type="file", accept="audio/mp3,video/mp4")
canvas#canvas
audio#audio(src="", controls="true")
window.AudioContext = window.AudioContext || window.webkitAudioContext;
class renderWave {
private _samples: number = 10000;
private _strokeStyle: string = "#3098ff";
constructor(message: any) {
this.audioContext = new AudioContext();
this.canvas = document.querySelector("canvas");
this.ctx = this.canvas.getContext("2d");
this.data = [];
message
.then(arrayBuffer => {
return this.audioContext.decodeAudioData(arrayBuffer);
})
.then(audioBuffer => {
this.draw(this.normalizedData(audioBuffer));
this.drawData(this.data);
});
}
normalizedData(audioBuffer) {
const rawData = audioBuffer.getChannelData(0); // We only need to work with one channel of data
const samples = this._samples; // Number of samples we want to have in our final data set
const blockSize = Math.floor(rawData.length / samples); // Number of samples in each subdivision
const filteredData = [];
for (let i = 0; i < samples; i++) {
filteredData.push(rawData[i * blockSize]);
}
return filteredData;
}
draw(normalizedData) {
// set up the canvas
const canvas = this.canvas;
const dpr = window.devicePixelRatio || 1;
const padding = 10;
canvas.width = canvas.offsetWidth * dpr;
canvas.height = (canvas.offsetHeight + padding * 2) * dpr;
this.ctx.scale(dpr, dpr);
this.ctx.translate(0, canvas.offsetHeight / 2 + padding); // set Y = 0 to be in the middle of the canvas
// draw the line segments
const width = canvas.offsetWidth / normalizedData.length;
for (let i = 0; i < normalizedData.length; i++) {
const x = width * i;
let height = normalizedData[i] * canvas.offsetHeight - padding;
if (height < 0) {
height = 0;
} else if (height > canvas.offsetHeight / 2) {
height = height > canvas.offsetHeight / 2;
}
// this.drawLineSegment(this.ctx, x, height, width, (i + 1) % 2);
this.data.push({
x: x,
h: height,
w: width,
isEven: (i + 1) % 2
});
}
return this.data;
}
drawLineSegment(
ctx: object,
x: number,
height: number,
width: number,
isEven: boolean,
colors: string = this._strokeStyle
) {
ctx.lineWidth = 1; // how thick the line is
ctx.strokeStyle = colors; // what color our line is
ctx.beginPath();
height = isEven ? height : -height;
ctx.moveTo(x, 0);
ctx.lineTo(x + width, height);
ctx.stroke();
}
drawData(data: array, colors: string = this._strokeStyle) {
data.map(item => {
this.drawLineSegment(
this.ctx,
item.x,
item.h,
item.w,
item.isEven,
colors
);
});
}
drawTimeline(percent: number) {
let end: number = Math.ceil(this._samples * percent);
let start: number = end - 5 || 0;
let t = this.data.slice(0, end);
this.drawData(t, "#1d1e22");
}
}
document.getElementById("fileinput").addEventListener("change", function() {
var wave = new renderWave(this.files[0].arrayBuffer());
var audioPlayer = document.getElementById("audio");
audioPlayer.src = URL.createObjectURL(this.files[0]);
// audioPlayer.play();
audioPlayer.ontimeupdate = function() {
let percent = this.currentTime / this.duration;
wave.drawTimeline(percent);
};
});
<script src="https://codepen.io/bataimx/pen/JjdyXyG"></script>
canvas {
width: 100%;
height: 130px;
background: #f9f9f9;
margin: 2rem auto;
box-shadow: inset 0px 0px 25px -15px #000;
border-radius: 5px;
overflow: hidden;
}
.wrapper {
padding: 2rem;
}
<link href="https://codepen.io/bataimx/pen/JjdyXyG" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment