Skip to content

Commit

Permalink
fix one-shot retrigger; add touch events
Browse files Browse the repository at this point in the history
  • Loading branch information
mark-mxwl committed Mar 25, 2024
1 parent c54ef2e commit 08ac268
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 52 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "grimy",
"description": "Multimode distortion built with Web Audio.",
"private": true,
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"scripts": {
"dev": "vite --port 5001 --strictPort",
Expand Down
67 changes: 34 additions & 33 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import Knob from "./components/Knob.jsx";
import DragDrop from "./components/DragDrop.jsx";
import InfoModal from "./components/InfoModal.jsx";
Expand All @@ -26,15 +26,14 @@ let chromeAgent = navigator.userAgent.indexOf("Chrome") > -1;

export default function App() {
const [uploadedAudio, setUploadedAudio] = useState(null);
const [bufferReady, setBufferReady] = useState(false);
const audioNodeIsPlaying = useRef(false);
const [fXAmount, setFXAmount] = useState(5000);

const [midiCC, setMidiCC] = useState(0);
const [midiValue, setMidiValue] = useState(0);
const [useMidi, setUseMidi] = useState(false);
const [midiDeviceName, setMidiDeviceName] = useState("");

const [toggle, setToggle] = useState(false);
const [toggleFilter, setToggleFilter] = useState(true);
const [isVisible, setIsVisible] = useState(false);

Expand Down Expand Up @@ -106,28 +105,24 @@ export default function App() {
reader1.onload = function (e) {
ctx.decodeAudioData(e.target.result).then(function (buffer) {
currentBuffer = buffer;
setBufferReady(true);
setToggle((prev) => (prev = !prev));
});
};
}
}, [uploadedAudio]);

// SOURCE NODE
useEffect(() => {
if (bufferReady) {
const soundSource = ctx.createBufferSource();
soundSource.buffer = currentBuffer;
soundSource.connect(distortion);
playBufferedSample = () => soundSource.start();
stopBufferedSample = () => soundSource.stop();
loopBufferedSample = () => {
soundSource.loop = true;
soundSource.loopEnd = currentBuffer.duration;
};
bufferLength = Number(soundSource.buffer.duration.toFixed(0) * 1000);
}
}, [toggle]);
function createSourceNode() {
const soundSource = ctx.createBufferSource();
soundSource.buffer = currentBuffer;
soundSource.connect(distortion);
playBufferedSample = () => soundSource.start();
stopBufferedSample = () => soundSource.stop();
loopBufferedSample = () => {
soundSource.loop = true;
soundSource.loopEnd = currentBuffer.duration;
};
bufferLength = Number(soundSource.buffer.duration.toFixed(0) * 1000);
}

// TOGGLE FILTER
useEffect(() => {
Expand All @@ -154,7 +149,9 @@ export default function App() {
access.addEventListener("statechange", findMidiDevices);
const inputs = access.inputs;
inputs.forEach((input) => {
input.addEventListener("midimessage", handleMidiInput);
input.onmidimessage = (message) => {
handleMidiInput(message);
};
});
},
(fail) => {
Expand All @@ -175,15 +172,15 @@ export default function App() {
function handleMidiInput(e) {
let buttons = false;
// MIDI note CC: 48 (PLAY), 49 (STOP), 50 (LOOP)
if (e.data[1] === 48 && e.data[2] >= 1) {
if (e.data[1] === 48 && e.data[2] > 0) {
playSample();
buttons = true;
}
if (e.data[1] === 49 && e.data[2] >= 1) {
if (e.data[1] === 49 && e.data[2] > 0) {
stopSample();
buttons = true;
}
if (e.data[1] === 50 && e.data[2] >= 1) {
if (e.data[1] === 50 && e.data[2] > 0) {
loopSample();
buttons = true;
}
Expand All @@ -209,25 +206,29 @@ export default function App() {
}

function playSample() {
ctx.resume();
playBufferedSample();
setTimeout(suspendContext, bufferLength);
if (audioNodeIsPlaying.current === true) {
stopSample();
createSourceNode();
playBufferedSample();
audioNodeIsPlaying.current = true;
}
if (audioNodeIsPlaying.current === false) {
createSourceNode();
playBufferedSample();
audioNodeIsPlaying.current = true;
}
}

function stopSample() {
stopBufferedSample();
suspendContext();
audioNodeIsPlaying.current = false;
}

function loopSample() {
ctx.resume();
createSourceNode();
playBufferedSample();
loopBufferedSample();
}

function suspendContext() {
ctx.suspend();
setToggle((prev) => (prev = !prev));
audioNodeIsPlaying.current = true;
}

return (
Expand Down
2 changes: 1 addition & 1 deletion src/components/InfoModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function InfoModal(props) {
</p>
<p>
FILE SELECTOR {">> "}
Press any key to select an audio file from your device.
Press SPACEBAR or ENTER to select an audio file from your device.
</p>
<p>
MODE {">> "}
Expand Down
38 changes: 22 additions & 16 deletions src/components/Knob.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,30 +66,36 @@ export default function Knob(props) {
}

function mountKnob() {
knobRef.current.addEventListener("mousedown", (e) => {
center = e.pageY;
mouseIsDown = true;
});
["mousedown", "touchstart"].forEach((e) =>
knobRef.current.addEventListener(e, (e) => {
center = e.pageY;
mouseIsDown = true;
})
);

document.body.addEventListener("mouseup", (e) => {
mouseIsDown = false;
});
["mouseup", "touchend"].forEach((e) =>
document.body.addEventListener(e, (e) => {
mouseIsDown = false;
})
);

knobRef.current.addEventListener("mouseenter", (e) => {
if (mouseIsDown) {
mouseIsMoving = true;
}
});

document.body.addEventListener("mousemove", (e) => {
mouseIsMoving = true;
if (mouseIsDown && mouseIsMoving) {
distance = distClamp((center - e.pageY) * 38, 5000, -4900);
knobRef.current.style.transform = "rotate(" + distance / 32 + "deg)";
currentValueRef.current = distance + 5000;
setFXAmount(distance + 5000);
}
});
["mousemove", "touchmove"].forEach((e) =>
document.body.addEventListener(e, (e) => {
mouseIsMoving = true;
if (mouseIsDown && mouseIsMoving) {
distance = distClamp((center - e.pageY) * 38, 5000, -4900);
knobRef.current.style.transform = "rotate(" + distance / 32 + "deg)";
currentValueRef.current = distance + 5000;
setFXAmount(distance + 5000);
}
})
);

knobRef.current.addEventListener("dblclick", (e) => {
knobRef.current.style.transform = "rotate(0deg)";
Expand Down

0 comments on commit 08ac268

Please sign in to comment.