Published
Edited
Dec 14, 2021
2 forks
8 stars
Insert cell
Insert cell
Insert cell
Insert cell
// Here is the fixed scale we will use, you can try other scales/notes.
// notes = ["F#4", "E4", "C#4", "A4"];
// notes = ["A3", "C4", "D4", "E3", "G4"];
notes = ['A3', 'C4', 'D4', 'E4', 'G4', 'A4'];
// notes = ['A3', 'C4', 'D4', 'A3', 'E4', 'G4', 'A4'];

// notes = [ "A4", "D3", "E3", "G4", 'F#4' ];
// notes = ["C5", "A3", "D4", "G4", "A4", "F4"];
// notes = ["F#4", "E4", "C#4", "A4", "A2", "C4", "D4", "E2", "G4" ];
Insert cell
Tone = soundEnabled, require('tone')
Insert cell
Insert cell
Insert cell
volume = -30 // Master volume in decior audio
Insert cell
// Number of rows is the number of different notes
numRows = notes.length;
Insert cell
// Number of columns is depending on how many notes to play in a measure
numCols = 16;
Insert cell
noteInterval = `${numCols}n`; // default hardcoded to 16
Insert cell
noteIndices = d3.range(numCols);
Insert cell
dim = Math.min(width, height)
Insert cell
// Setup a synth with ToneJS
// We use a poly synth which can hold up to numRows voices
// Then we will play each note on a different voice

synth = new Tone.PolySynth({numRows}, Tone.DuoSynth)
Insert cell
music = ({
playing: false, // Whether the audio sequence is playing
sequence: 0, // The current Tone.Sequence
currentColumn: 0, // The currently playing column
cancelRepeat: 0, // event thst drives the repeat and need to be reset to stop multi events from being made.
})
Insert cell
Insert cell
// // Here is where we actually play the audio
function onSequenceStep(time, column) { // We build up a list of notes, which will equal the numRows. This gets passed into our PolySynth
let notesToPlay = [];

musicData.forEach((row, rowIndex) => { // Go through each row
if (row[column] == 1) { // See if the note is "on". If its on, add it to the list of notes to play
const note = notes[rowIndex];
notesToPlay.push(note);
}
});

// Trigger a note
const velocity = d3.randomUniform(0.5,1.5)();
synth.triggerAttackRelease(notesToPlay, noteInterval, time, velocity);
Tone.Draw.schedule(function () { return music.currentColumn = column; }, time); // sets and uses the current coloum
}
Insert cell
// Create a Row*Col data structure that has nested arrays filled with zeros
musicData = d3.range(numRows).map( ()=> d3.range(numCols).map ( ()=>0) )
Insert cell
Insert cell
// the orgional re sequencer

function reSequence() {
const chance = d3.randomUniform(0.5,1.5)() // Choose a % chance so that sometimes it is more busy, other times more sparse
for (let y = 0; y < musicData.length; y++) { // Loop through and create some random on/off values
const row = musicData[y];
for (let x = 0; x < row.length; x++) {
row[x] = Math.abs(d3.randomNormal()()) > chance ? 1 : 0; // added Math.abs to gen more notes
}
// Loop through again and make sure we don't have two consectutive on values (it sounds bad)
for (let x = 0; x < row.length - 1; x++) {
if (row[x] === 1 && row[x + 1] === 1) {
row[x + 1] = 0;
x++;
}
}
}
}
Insert cell
// starting one track at a time gives a smooth tune intoduction

function newSeq() {
const chance = d3.randomUniform(0.5,1.5)() // Choose a % chance so that sometimes it is more busy, other times more sparse

// to change one track at a time i need to cycle through the track and not update all the tracks at one time.
// for (let track = 0; track < musicData.length; track++) { // Loop through the for tracks and create some random on/off values

const row = musicData[tuneData.track%notes.length];

tuneData.track++
// update the row.
for (let x = 0; x < row.length; x++) {
row[x] = Math.abs(d3.randomNormal()()) > chance ? 1 : 0; // added Math.abs to gen more notes
}
// Loop through again and make sure we don't have two consectutive on values (as it sounds bad)
for (let x = 0; x < row.length - 1; x++) {
if (row[x] === 1 && row[x + 1] === 1) {
row[x + (d3.randomInt(2)())] = 0; // was row[x + 1] = 0; but the random adds in double notes
// x++;
}
}
//} // next track
}
Insert cell
tuneData = ({
track:0,
tracks: musicData
})
Insert cell
height = width/3
Insert cell
PI = Math.PI
Insert cell
TWO_PI = PI*2
Insert cell
// Tone = require('tone')
Insert cell
import {p5} from "@tmcw/p5"
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more