Browse Source

feat: Added basic version of earcon sonifications.

master
Denis Thiessen 4 months ago
parent
commit
fbf3537585
  1. 15
      src/components/DetailElement.jsx
  2. 10
      src/components/ScrollableTab.jsx
  3. 93
      src/core/audio/AudioHandler.jsx
  4. 112
      src/core/audio/MotifHandler.jsx
  5. BIN
      src/core/audio/samples/flute_samples/A#3.wav
  6. BIN
      src/core/audio/samples/flute_samples/A#4.wav
  7. BIN
      src/core/audio/samples/flute_samples/A2.wav
  8. BIN
      src/core/audio/samples/flute_samples/C2.wav
  9. BIN
      src/core/audio/samples/flute_samples/C3.mp3
  10. BIN
      src/core/audio/samples/flute_samples/C3.wav
  11. BIN
      src/core/audio/samples/flute_samples/C4.wav
  12. BIN
      src/core/audio/samples/flute_samples/D2.wav
  13. BIN
      src/core/audio/samples/flute_samples/D3.wav
  14. BIN
      src/core/audio/samples/flute_samples/D4.wav
  15. BIN
      src/core/audio/samples/flute_samples/F#2.wav
  16. BIN
      src/core/audio/samples/flute_samples/F#3.wav
  17. BIN
      src/core/audio/samples/flute_samples/F#4.wav
  18. BIN
      src/core/audio/samples/viola.wav
  19. 2
      src/index.css

15
src/components/DetailElement.jsx

@ -41,7 +41,8 @@ const calculateDensities = function (el) {
var textLength = 0; var textLength = 0;
var linkAmount = 0; var linkAmount = 0;
var listAmount = 0; var listAmount = 0;
var textDensity = 4;
var textDensity = 0;
var linkDensity = 0; var linkDensity = 0;
const childElements = getChildElementRoot(el); const childElements = getChildElementRoot(el);
@ -90,22 +91,20 @@ const calculateDensities = function (el) {
linkAmount = elementAmounts.linkAmount; linkAmount = elementAmounts.linkAmount;
listAmount = elementAmounts.listAmount; listAmount = elementAmounts.listAmount;
if (textLength < 900 && textLength > 600) {
if (textLength > 600) {
textDensity = 3; textDensity = 3;
} else if (textLength < 600 && textLength > 300) {
} else if (textLength <= 600 && textLength >= 300) {
textDensity = 2; textDensity = 2;
} else if (textLength < 300 && textLength > 0) { } else if (textLength < 300 && textLength > 0) {
textDensity = 1; textDensity = 1;
} else if (textLength === 0) {
textDensity = 0;
} }
if (linkAmount > 3) { if (linkAmount > 3) {
linkDensity = 4;
} else if (linkAmount === 2 || linkAmount === 3) {
linkDensity = 3; linkDensity = 3;
} else if (linkAmount === 1) {
} else if (linkAmount === 2 || linkAmount === 3) {
linkDensity = 2; linkDensity = 2;
} else if (linkAmount === 1) {
linkDensity = 1;
} }
return { textDensity: textDensity, linkDensity: linkDensity }; return { textDensity: textDensity, linkDensity: linkDensity };

10
src/components/ScrollableTab.jsx

@ -59,13 +59,13 @@ function ScrollableTab({ onActiveTabChange, sonificationType, children }) {
} }
if (type === "model") { if (type === "model") {
playTabModelSonification(panVal, frequencyVal, false);
playTabModelSonification(panVal, frequencyVal, !rightSideEnd);
} else if (type === "earcon") { } else if (type === "earcon") {
playTabEarconSonification();
playTabEarconSonification(!rightSideEnd, key, amountTabs);
} }
if (reachedEndValue && endValueVisible) { if (reachedEndValue && endValueVisible) {
playTabModelSonification(panVal, frequencyVal, true);
// Chord End sound play... :)
if (rightSideEnd) { if (rightSideEnd) {
endElement = 1; endElement = 1;
rightSideEnd = false; rightSideEnd = false;
@ -103,8 +103,8 @@ function ScrollableTab({ onActiveTabChange, sonificationType, children }) {
return ( return (
<Tab <Tab
id={tabId} id={tabId}
onTouchStart={() => onHoverTab(tabId)}
onMouseOver={() => onHoverTab(tabId)}
//onTouchMove={() => onHoverTab(tabId)}
//onMouseOver={() => onHoverTab(tabId)}
key={tabId} key={tabId}
actionType={k.props.actionType} actionType={k.props.actionType}
redirectLoc={k.props.redirectLoc} redirectLoc={k.props.redirectLoc}

93
src/core/audio/AudioHandler.jsx

@ -1,11 +1,46 @@
import * as Tone from "tone"; import * as Tone from "tone";
import { pushToSonificationLog } from "../log/SensorLogger"; import { pushToSonificationLog } from "../log/SensorLogger";
import drumSample from "./samples/snare_drum.wav"; import drumSample from "./samples/snare_drum.wav";
import C2Flute from "./samples/flute_samples/C2.wav";
import C3Flute from "./samples/flute_samples/C3.wav";
import C4Flute from "./samples/flute_samples/C4.wav";
import A2Flute from "./samples/flute_samples/A2.wav";
import ASharp3Flute from "./samples/flute_samples/A#3.wav";
import ASharp4Flute from "./samples/flute_samples/A#4.wav";
import D2Flute from "./samples/flute_samples/D2.wav";
import D3Flute from "./samples/flute_samples/D3.wav";
import D4Flute from "./samples/flute_samples/D4.wav";
import FSharp2Flute from "./samples/flute_samples/F#2.wav";
import FSharp3Flute from "./samples/flute_samples/F#3.wav";
import FSharp4Flute from "./samples/flute_samples/F#4.wav";
import violaSample from "./samples/viola.wav";
import { getMotifByVersion } from "./MotifHandler";
import { generateChord } from "./ChordHelper";
const drumSynth = new Tone.Sampler({ const drumSynth = new Tone.Sampler({
C3: drumSample, C3: drumSample,
}).toDestination(); }).toDestination();
const violaSynth = new Tone.Sampler({
C5: violaSample,
}).toDestination();
const fluteSynth = new Tone.Sampler({
C2: C2Flute,
C3: C3Flute,
C4: C4Flute,
A2: A2Flute,
"A#3": ASharp3Flute,
"A#4": ASharp4Flute,
D2: D2Flute,
D3: D3Flute,
D4: D4Flute,
"F#2": FSharp2Flute,
"F#3": FSharp3Flute,
"F#4": FSharp4Flute,
}).toDestination();
const setRotation = (angle) => { const setRotation = (angle) => {
Tone.Listener.forwardX.value = Math.sin(angle); Tone.Listener.forwardX.value = Math.sin(angle);
Tone.Listener.forwardY.value = 0; Tone.Listener.forwardY.value = 0;
@ -18,6 +53,22 @@ const setRotationRamp = (panner, angle, rampTime) => {
panner.positionZ.rampTo(-Math.cos(angle), rampTime); panner.positionZ.rampTo(-Math.cos(angle), rampTime);
}; };
// First three are small, then medium, then large and the link chooses the corresponding motif within the category...
const calculateDetailMotifVersion = function (textDensity, linkDensity) {
return 3 * textDensity + linkDensity;
};
const tabChordProgression = [
generateChord(60, "major"),
generateChord(62, "minor"),
generateChord(64, "minor"),
generateChord(65, "major"),
generateChord(67, "major"),
generateChord(69, "minor"),
generateChord(71, "diminished"),
generateChord(72, "major"),
];
var offCooldown = true; var offCooldown = true;
const playChordEndSound = function () { const playChordEndSound = function () {
@ -42,14 +93,30 @@ const playChordEndSound = function () {
} }
}; };
const calculatePlayedTabChord = function (endReached, tabIndex, tabAmount) {
const progress = tabIndex / (tabAmount - 1);
const chordListLength = tabChordProgression.length;
if (!endReached) {
return Math.floor(progress * (chordListLength - 1));
}
return Math.floor((1 - progress) * (chordListLength - 1)) + 1;
};
// Maybe also dat... // Maybe also dat...
export function playTabEarconSonification() {
export function playTabEarconSonification(endReached, tabIndex, tabAmount) {
if (offCooldown) { if (offCooldown) {
var loop = new Tone.Loop((time) => {
drumSynth.triggerAttackRelease("C3", "4n", time);
}, "4n");
loop.iterations = 2;
loop.start();
const now = Tone.now();
const tabChordIndex = calculatePlayedTabChord(
endReached,
tabIndex,
tabAmount
);
fluteSynth.triggerAttackRelease(
tabChordProgression[tabChordIndex],
"4n",
now
);
Tone.Transport.start(); Tone.Transport.start();
pushToSonificationLog("tab_earcon"); pushToSonificationLog("tab_earcon");
offCooldown = false; offCooldown = false;
@ -60,8 +127,7 @@ export function playTabEarconSonification() {
} }
export function playTabModelSonification(pannerVal, frequencyVal, endReached) { export function playTabModelSonification(pannerVal, frequencyVal, endReached) {
if(endReached) {
if (endReached) {
playChordEndSound(); playChordEndSound();
return; return;
} }
@ -97,12 +163,11 @@ export function playTabModelSonification(pannerVal, frequencyVal, endReached) {
// Menu Sonification Paper. :) // Menu Sonification Paper. :)
export function playDetailEarconSonification(textDensity, linkDensity) { export function playDetailEarconSonification(textDensity, linkDensity) {
if (offCooldown) { if (offCooldown) {
var loop = new Tone.Loop((time) => {
drumSynth.triggerAttackRelease("C3", "4n", time);
}, "4n");
loop.iterations = 2;
loop.start();
drumSynth.context.resume();
const synth = violaSynth.toDestination();
getMotifByVersion(
calculateDetailMotifVersion(textDensity, linkDensity),
synth
);
Tone.Transport.start(); Tone.Transport.start();
pushToSonificationLog("detail_earcon"); pushToSonificationLog("detail_earcon");
offCooldown = false; offCooldown = false;

112
src/core/audio/MotifHandler.jsx

@ -0,0 +1,112 @@
import * as Tone from "tone";
// List of all Earcon-based Motifs used by the AudioHandler.
const motifs = [
getMotif1,
getMotif2,
getMotif3,
getMotif4,
getMotif5,
getMotif6,
getMotif7,
getMotif8,
getMotif9,
getMotif10,
getMotif11,
getMotif12,
];
// Small Motifs
export function getMotif1(synth) {
const now = Tone.now();
synth.triggerAttackRelease("A3", "4n", now);
synth.triggerAttackRelease("C#4", "4n", now + 0.16);
}
export function getMotif2(synth) {
const now = Tone.now();
synth.triggerAttackRelease("A3", "4n", now);
synth.triggerAttackRelease("E#4", "4n", now + 0.16);
}
export function getMotif3(synth) {
const now = Tone.now();
synth.triggerAttackRelease("A3", "4n", now);
synth.triggerAttackRelease("G#4", "4n", now + 0.16);
}
export function getMotif4(synth) {
const now = Tone.now();
synth.triggerAttackRelease("A3", "4n", now);
synth.triggerAttackRelease("A4", "4n", now + 0.16);
}
// Medium Motifs
export function getMotif5(synth) {
const now = Tone.now();
synth.triggerAttackRelease("A3", "4n", now);
synth.triggerAttackRelease("C#4", "4n", now + 0.08);
synth.triggerAttackRelease("E4", "4n", now + 0.24);
}
export function getMotif6(synth) {
const now = Tone.now();
synth.triggerAttackRelease("A3", "4n", now);
synth.triggerAttackRelease("C#4", "4n", now + 0.08);
synth.triggerAttackRelease("G#4", "4n", now + 0.24);
}
export function getMotif7(synth) {
const now = Tone.now();
synth.triggerAttackRelease("A3", "4n", now);
synth.triggerAttackRelease("E4", "4n", now + 0.16);
synth.triggerAttackRelease("A4", "4n", now + 0.32);
}
export function getMotif8(synth) {
const now = Tone.now();
synth.triggerAttackRelease("A3", "4n", now);
synth.triggerAttackRelease("G#4", "4n", now + 0.08);
synth.triggerAttackRelease("C#5", "4n", now + 0.24);
}
// Large Motifs
export function getMotif9(synth) {
const now = Tone.now();
synth.triggerAttackRelease("C#3", "4n", now);
synth.triggerAttackRelease("C#4", "4n", now + 0.16);
synth.triggerAttackRelease("G#3", "4n", now + 0.32);
synth.triggerAttackRelease("G#4", "4n", now + 0.48);
}
export function getMotif10(synth) {
const now = Tone.now();
synth.triggerAttackRelease("E3", "4n", now);
synth.triggerAttackRelease("E4", "4n", now + 0.16);
synth.triggerAttackRelease("G#3", "4n", now + 0.32);
synth.triggerAttackRelease("C#4", "4n", now + 0.48);
}
export function getMotif11(synth) {
const now = Tone.now();
synth.triggerAttackRelease("E3", "4n", now);
synth.triggerAttackRelease("E4", "4n", now + 0.16);
synth.triggerAttackRelease("A3", "4n", now + 0.32);
synth.triggerAttackRelease("C#4", "4n", now + 0.48);
}
export function getMotif12(synth) {
const now = Tone.now();
synth.triggerAttackRelease("A3", "4n", now);
synth.triggerAttackRelease("A4", "4n", now + 0.16);
synth.triggerAttackRelease("C#4", "4n", now + 0.32);
synth.triggerAttackRelease("E4", "4n", now + 0.48);
}
export function getMotifByVersion(versionNumber, synth) {
return motifs[versionNumber](synth);
}

BIN
src/core/audio/samples/flute_samples/A#3.wav

BIN
src/core/audio/samples/flute_samples/A#4.wav

BIN
src/core/audio/samples/flute_samples/A2.wav

BIN
src/core/audio/samples/flute_samples/C2.wav

BIN
src/core/audio/samples/flute_samples/C3.mp3

BIN
src/core/audio/samples/flute_samples/C3.wav

BIN
src/core/audio/samples/flute_samples/C4.wav

BIN
src/core/audio/samples/flute_samples/D2.wav

BIN
src/core/audio/samples/flute_samples/D3.wav

BIN
src/core/audio/samples/flute_samples/D4.wav

BIN
src/core/audio/samples/flute_samples/F#2.wav

BIN
src/core/audio/samples/flute_samples/F#3.wav

BIN
src/core/audio/samples/flute_samples/F#4.wav

BIN
src/core/audio/samples/viola.wav

2
src/index.css

@ -30,7 +30,7 @@ code {
} }
canvas { canvas {
opacity: 1;
opacity: 0;
z-index: 999; z-index: 999;
} }

Loading…
Cancel
Save