Skip to content

Commit c7569b3

Browse files
committed
more visualizations
1 parent 8660d0a commit c7569b3

15 files changed

+12517
-72
lines changed

packages/bargraph/README.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# crossaudio bargraph
2+
3+
This allows you to draw a bargraph to a canvas.
4+
5+
## usage
6+
7+
Install in your web-project with `npm i @crossaudio/bargraph`. Use it in a synth, like this:
8+
9+
Put this in your HTML:
10+
11+
```html
12+
<canvas id="canvas" width="960" height="300" /><br />
13+
<label><span>frequency:</span> <input type="range" min="0" max="127" id="frequency"></label>
14+
```
15+
16+
And this in your script:
17+
18+
```js
19+
const mtof = note => 440 * Math.pow(2, (note - 69) / 12)
20+
21+
const synth = (context, params) => {
22+
const bargraph = new Bargraph(context, document.getElementById('canvas'))
23+
bargraph.start()
24+
25+
const vco = context.createOscillator()
26+
vco.frequency.value = mtof(params.frequency)
27+
vco.start()
28+
29+
params.on('frequency', value => {
30+
vco.frequency.value = mtof(value)
31+
})
32+
33+
vco.connect(bargraph)
34+
bargraph.connect(context.destination)
35+
}
36+
37+
// play synth
38+
const params = new Params({
39+
frequency: 48
40+
})
41+
play(synth, params)
42+
43+
// hook up the UI
44+
const freq = document.getElementById('frequency')
45+
freq.onchange = e => { params.frequency = freq.value }
46+
```
47+
48+
## TODO
49+
50+
- You might also be able to use it in [node-canvas](https://github.com/Automattic/node-canvas), test, and if it works, add an example

packages/bargraph/bargraph-demo.mp3

4.2 MB
Binary file not shown.

packages/bargraph/index.html

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<head>
2+
<title>@crossaudio/bargraph</title>
3+
<style>
4+
body {
5+
width: 95%;
6+
max-width: 1024px;
7+
margin: auto;
8+
font-family: sans-serif;
9+
padding: 20px;
10+
}
11+
label {
12+
display: block;
13+
}
14+
canvas {
15+
width: 100%;
16+
background: black;
17+
}
18+
</style>
19+
</head>
20+
<body>
21+
<h1>@crossaudio/bargraph</h1>
22+
<p>This is a simple demo of <a href="https://github.com/konsumer/crossaudio/tree/main/packages/bargraph">@crossaudio/bargraph</a>. View the source to see how it works. It uses plain javascript, but could be loaded in react, or even some native implementation of canvas. Due to browser-security, you will need to click on the page to start the analysis. The test-music is <a href="https://soundcloud.com/mokhov/midnight-love">Mokhov - Midnight Love</a></p>
23+
24+
<canvas id="canvas" width="960" height="300"></canvas>
25+
26+
<!-- use CDN to get these -->
27+
<script src="https://unpkg.com/@crossaudio/core@latest"></script>
28+
<script src="https://unpkg.com/@crossaudio/bargraph@latest"></script>
29+
<!-- <script src="dist/crossaudio-bargraph.umd.js"></script> -->
30+
31+
<script>
32+
// these were added by script-tags, above
33+
const { Bargraph } = window.bargraph
34+
const { play } = window.core
35+
36+
// standard synth definition
37+
const synth = async (context, params) => {
38+
// TODO: wrap this in crossaudio
39+
const audio = new Audio('bargraph-demo.mp3')
40+
audio.loop = true
41+
audio.play()
42+
const source = context.createMediaElementSource(audio)
43+
44+
const bargraph = new Bargraph(context, document.getElementById('canvas'))
45+
bargraph.start()
46+
47+
source.connect(bargraph)
48+
bargraph.connect(context.destination)
49+
}
50+
51+
play(synth)
52+
</script>
53+
</body>

packages/bargraph/index.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* global AnalyserNode */
2+
3+
import interpolate from 'color-interpolate'
4+
5+
// "jet" from https://www.npmjs.com/package/colormap
6+
const defaultPallette = ['#000083', '#000787', '#000d8c', '#001490', '#001b94', '#002199', '#00289d', '#002fa1', '#0035a6', '#003caa', '#0047af', '#0152b3', '#015db8', '#0167bd', '#0172c2', '#027dc6', '#0288cb', '#0293d0', '#039ed5', '#03a8d9', '#03b3de', '#03bee3', '#04c9e7', '#04d4ec', '#04dff1', '#04e9f6', '#05f4fa', '#05ffff', '#14fff0', '#22ffe1', '#31ffd2', '#40ffc3', '#4fffb4', '#5dffa5', '#6cff96', '#7bff87', '#89ff78', '#98ff69', '#a7ff5a', '#b5ff4b', '#c4ff3c', '#d3ff2d', '#e2ff1e', '#f0ff0f', '#ffff00', '#fff100', '#fee300', '#fed500', '#fec600', '#feb800', '#fdaa00', '#fd9c00', '#fd8e00', '#fd8000', '#fc7100', '#fc6300', '#fc5500', '#fb4700', '#fb3900', '#fb2a00', '#fb1c00', '#fa0e00', '#fa0000', '#ec0000', '#df0000', '#d10000', '#c40000', '#b60000', '#a90000', '#9b0000', '#8e0000', '#800000']
7+
8+
export class Bargraph extends AnalyserNode {
9+
constructor (audioContext, canvas, fftSize = 256, palette = defaultPallette, backgroundColor = 'black', maxDecibels = -25, minDecibels = -60, smoothingTimeConstant = 0.5) {
10+
super(audioContext, { maxDecibels, minDecibels, smoothingTimeConstant, fftSize })
11+
this.canvas = canvas
12+
this.canvasContext = canvas.getContext('2d')
13+
this.palette = palette
14+
this.backgroundColor = backgroundColor
15+
16+
// set this to false to stop the loop
17+
this.drawing = true
18+
}
19+
20+
start () {
21+
// setup a per-animation-frame loop to keep it updated
22+
this.data = new Uint8Array(this.frequencyBinCount)
23+
this.colormap = interpolate(this.palette)
24+
25+
const draw = () => {
26+
if (this.drawing) {
27+
window.requestAnimationFrame(draw)
28+
}
29+
this.getByteFrequencyData(this.data)
30+
31+
this.canvasContext.fillStyle = this.backgroundColor
32+
this.canvasContext.fillRect(0, 0, this.canvas.width, this.canvas.height)
33+
34+
const barWidth = (this.canvas.width / this.frequencyBinCount) * 2.5
35+
let barHeight
36+
let x = 0
37+
38+
for (let i = 0; i < this.frequencyBinCount; i++) {
39+
const d = parseFloat(this.data[i]) / 255.0
40+
barHeight = d * this.canvas.height
41+
const color = this.colormap(d)
42+
this.canvasContext.fillStyle = color
43+
this.canvasContext.fillRect(x, this.canvas.height - barHeight, barWidth, barHeight)
44+
x += barWidth + 1
45+
}
46+
}
47+
draw()
48+
}
49+
50+
stop () {
51+
this.drawing = false
52+
}
53+
}

0 commit comments

Comments
 (0)