Skip to content

Commit 496cca8

Browse files
committed
Added internal midi clock hook using a web worker
1 parent dd68fec commit 496cca8

File tree

6 files changed

+111
-3
lines changed

6 files changed

+111
-3
lines changed

package-lock.json

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@
2323
"uniqid": "^5.0.3"
2424
},
2525
"devDependencies": {
26-
"react": "^16.8.6",
27-
"webpack": "^4.29.6",
2826
"@babel/cli": "^7.2.3",
2927
"@babel/core": "^7.4.0",
3028
"@babel/plugin-proposal-object-rest-spread": "^7.4.0",
3129
"@babel/preset-env": "^7.4.2",
3230
"babel-loader": "^8.0.5",
33-
"webpack-cli": "^3.3.0"
31+
"react": "^16.8.6",
32+
"webpack": "^4.29.6",
33+
"webpack-cli": "^3.3.0",
34+
"worker-loader": "^2.0.0"
3435
},
3536
"repository": {
3637
"type": "git",

src/clock.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useState, useEffect } from 'react';
2+
import Worker from './clock.worker';
3+
4+
// Look into terminating worker when component is unmounted
5+
const worker = new Worker();
6+
7+
export const useInternalMIDIClock = (output, division = 24, initialTempo = 120) => {
8+
const [step, setStep] = useState(0);
9+
const [isPlaying, setIsPlaying] = useState(false);
10+
const [tempo, setTempo] = useState(initialTempo);
11+
12+
// Use closure to keep track of steps
13+
const handleClockMessage = () => {
14+
let steps = 0;
15+
return () => {
16+
if (output) output.send([0xf8]);
17+
steps++;
18+
if (division === 1) setStep(steps);
19+
else if (steps % division === 0) setStep(Math.floor(steps / division));
20+
};
21+
};
22+
23+
useEffect(() => {
24+
if (isPlaying) {
25+
const listener = handleClockMessage();
26+
worker.addEventListener('message', listener);
27+
if (output) output.send([0xfa]);
28+
worker.postMessage({ type: 'start', tempo });
29+
return () => {
30+
worker.removeEventListener('message', listener);
31+
if (output) output.send([0xfc]);
32+
worker.postMessage({ type: 'stop' });
33+
setStep(0);
34+
};
35+
}
36+
}, [isPlaying]);
37+
38+
useEffect(() => {
39+
worker.postMessage({ type: 'setTempo', tempo });
40+
}, [tempo]);
41+
42+
return [step, isPlaying, setIsPlaying, setTempo];
43+
};

src/clock.worker.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
let timeoutId = 0;
2+
let tempo = 120;
3+
let startTime = 0;
4+
let tick = 0;
5+
onmessage = (e) => {
6+
switch (e.data.type) {
7+
case 'start':
8+
startTime = performance.now();
9+
tick = 0;
10+
postMessage(0);
11+
tick++;
12+
timeoutId = setTimeout(function step() {
13+
postMessage(0);
14+
tick++;
15+
timeoutId = setTimeout(step, timeToWait(performance.now()));
16+
}, timeToWait(performance.now()));
17+
break;
18+
case 'setTempo':
19+
tick = 0;
20+
startTime = performance.now();
21+
tempo = e.data.tempo;
22+
break;
23+
case 'stop':
24+
clearTimeout(timeoutId);
25+
break;
26+
default:
27+
break;
28+
}
29+
};
30+
31+
const timeToWait = (now) => (1000 / ((tempo / 60) * 24)) * tick - (now - startTime);

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { useState, useEffect } from 'react';
2+
import { useInternalMIDIClock } from './clock';
23
import uniqid from 'uniqid';
34

5+
export { useInternalMIDIClock };
46
export const useMIDI = () => {
57
const [connections, changeConnections] = useState({
68
inputs: [],

webpack.config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ module.exports = {
1919
},
2020
},
2121
},
22+
{
23+
test: /\.worker\.js$/,
24+
use: {
25+
loader: 'worker-loader',
26+
options: {
27+
inline: true,
28+
},
29+
},
30+
},
2231
],
2332
},
2433
externals: {

0 commit comments

Comments
 (0)