From 43d724e0be94b5aeac863dc2fbaa02c34ce15262 Mon Sep 17 00:00:00 2001 From: Denis Thiessen Date: Thu, 30 May 2024 00:29:12 +0200 Subject: [PATCH] feat: Added Tone.js --- package-lock.json | 32 +++++++++++++++ package.json | 1 + src/components/ScrollableTab.jsx | 3 -- src/core/audio/AudioHandler.jsx | 57 +++++++++++++++----------- src/core/audio/samples/snare_drum.wav | Bin 0 -> 28164 bytes 5 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 src/core/audio/samples/snare_drum.wav diff --git a/package-lock.json b/package-lock.json index dbd4249..e494c6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "react-router-dom": "^6.23.0", "react-scripts": "5.0.1", "react-tabs-scrollable": "^2.0.6", + "tone": "^15.0.4", "web-vitals": "^2.1.4", "zustand": "^4.5.2" } @@ -5370,6 +5371,18 @@ "node": ">= 4.0.0" } }, + "node_modules/automation-events": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/automation-events/-/automation-events-7.0.5.tgz", + "integrity": "sha512-Ni6vhZg0mKmVlew1kxWAzWL7QY1LYDdoYgp6yF9OHeskDrjyJp2SqoKoPQYeiMYjeIlbSpnxXm/JI55VcmX5Wg==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.2.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.19", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", @@ -16343,6 +16356,16 @@ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" }, + "node_modules/standardized-audio-context": { + "version": "25.3.72", + "resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.72.tgz", + "integrity": "sha512-Dvwu2NuqafQqWxUWoo6G9ze/cvVNlFDpmIOA8XjuItrfR0h/REjgjoYCT3Y7nbkUJKGoz8SqqVzR7JATQV4XeQ==", + "dependencies": { + "@babel/runtime": "^7.24.5", + "automation-events": "^7.0.5", + "tslib": "^2.6.2" + } + }, "node_modules/static-eval": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", @@ -17144,6 +17167,15 @@ "node": ">=0.6" } }, + "node_modules/tone": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/tone/-/tone-15.0.4.tgz", + "integrity": "sha512-Fr2xATgdkNhzwMZhrU0DXpkXQyambq73hjHRrBiC0Wkc6aPYRdmkySE9kRFAW878zgMiD+Lqvn/uNHt/7hbdnQ==", + "dependencies": { + "standardized-audio-context": "^25.3.70", + "tslib": "^2.3.1" + } + }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", diff --git a/package.json b/package.json index aae0c28..6418aae 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "react-router-dom": "^6.23.0", "react-scripts": "5.0.1", "react-tabs-scrollable": "^2.0.6", + "tone": "^15.0.4", "web-vitals": "^2.1.4", "zustand": "^4.5.2" }, diff --git a/src/components/ScrollableTab.jsx b/src/components/ScrollableTab.jsx index 7804124..987543d 100644 --- a/src/components/ScrollableTab.jsx +++ b/src/components/ScrollableTab.jsx @@ -24,8 +24,6 @@ function ScrollableTab({ children }) { const redirectLoc = buttonAttributes["redirectLoc"] ? buttonAttributes.getNamedItem("redirectLoc").value : ""; - console.log(actionType); - console.log(redirectLoc); if (actionType === "button") { window.location.href = redirectLoc; @@ -57,7 +55,6 @@ function ScrollableTab({ children }) { const panVal = rightSideEnd ? clamp(3 * ((-1 / amountTabs) * key + 1) - 2, -1, 1) : clamp(2 * ((-1 / amountTabs) * -1 * key + 1) - 4, -1, 1); // ((-1 / amountTabs) * key + 1); - console.log(panVal); const frequencyVal = rightSideEnd ? Math.round((key / amountTabs) * key + 6) : Math.round(((key - amountTabs) / amountTabs) * (key - amountTabs) + 6); diff --git a/src/core/audio/AudioHandler.jsx b/src/core/audio/AudioHandler.jsx index f23dccb..9dfa83e 100644 --- a/src/core/audio/AudioHandler.jsx +++ b/src/core/audio/AudioHandler.jsx @@ -1,33 +1,42 @@ -import { Component } from "react"; import * as Tone from "tone"; import { pushToSonificationLog } from "../log/SensorLogger"; +import drumSample from "./samples/snare_drum.wav"; -export default class AudioHandler extends Component { - playTabEarconSonification() { - pushToSonificationLog("tab_earcon"); - } +const drumSynth = new Tone.Sampler({ + C3: drumSample, +}).toDestination(); - playTabModelSonification() { - pushToSonificationLog("tab_model"); - } +const setRotation = (angle) => { + Tone.Listener.forwardX.value = Math.sin(angle); + Tone.Listener.forwardY.value = 0; + Tone.Listener.forwardZ.value = -Math.cos(angle); +}; - playDetailEarconSonification() { - pushToSonificationLog("detail_earcon"); - } +const setRotationRamp = (panner, angle, rampTime) => { + panner.positionX.rampTo(Math.sin(angle), rampTime); + panner.positionY.rampTo(0, rampTime); + panner.positionZ.rampTo(-Math.cos(angle), rampTime); +}; - playDetailModelSonification() { - pushToSonificationLog("detail_model"); - } +export function playTabEarconSonification() { + pushToSonificationLog("tab_earcon"); +} + +export function playTabModelSonification() { + var loop = new Tone.Loop((time) => { + drumSynth.triggerAttackRelease("C3", "4n", time); + }, "4n"); + loop.iterations = 2; + loop.start(); + drumSynth.context.resume(); + Tone.Transport.start(); + pushToSonificationLog("tab_model"); +} - setRotation(angle) { - Tone.Listener.forwardX.value = Math.sin(angle); - Tone.Listener.forwardY.value = 0; - Tone.Listener.forwardZ.value = -Math.cos(angle); - } +export function playDetailEarconSonification() { + pushToSonificationLog("detail_earcon"); +} - setRotationRamp(panner, angle, rampTime) { - panner.positionX.rampTo(Math.sin(angle), rampTime); - panner.positionY.rampTo(0, rampTime); - panner.positionZ.rampTo(-Math.cos(angle), rampTime); - } +export function playDetailModelSonification() { + pushToSonificationLog("detail_model"); } diff --git a/src/core/audio/samples/snare_drum.wav b/src/core/audio/samples/snare_drum.wav new file mode 100644 index 0000000000000000000000000000000000000000..a0f838326ade67e76f65978b739d00cc19e71a21 GIT binary patch literal 28164 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 HxBTKiC;o1B literal 0 HcmV?d00001