diff --git a/app.js b/app.js index 75cf0c8..aab2115 100644 --- a/app.js +++ b/app.js @@ -1,10 +1,3 @@ -import { - sopranoTones, altoTones, tenorTones, bassTones, - extractPitchset, -} from "./js/toneGeneration.js"; - -const pitchsets = [sopranoTones, altoTones, tenorTones, bassTones]; - // initialize four synth voices const soprano = new Tone.Synth().toDestination(); const alto = new Tone.Synth().toDestination(); @@ -20,47 +13,18 @@ export const audioTest = () => { } // allows a chord to be generated with input from another function -export const soundChord = (pitches) => { - const [s,a,t,b] = pitches; +// pitchNames: array of strings +export const soundChord = (pitchNames) => { + const [s,a,t,b] = pitchNames; soprano.triggerAttackRelease(s, "8n"); alto.triggerAttackRelease(a, "8n"); tenor.triggerAttackRelease(t, "8n"); bass.triggerAttackRelease(b, "8n"); } -// initial test: generate a single, random chord -export const fullRandomChord = () => { - let pitches = []; - for (let voice of pitchsets) { - // finds a random index, excluding any which may already exist in the array - let index; - - do { - index = Math.floor(Math.random() * 100) % voice.length; - } while (pitches.includes(voice[index])); - - pitches.push(voice[index]); - console.log(voice[index]); - } - - for (let i = 0; i < pitches.length; i++) { - if (pitches[i] === pitches[i+1]) { - console.log("CAUGHT"); - } - } - - soundChord(pitches); - extractPitchset(pitches); -} - // set up transport -let clock = 0; -let slowClock = 0; - const transportStart = document.getElementById('transport-start'); -let transport; - const loop = new Tone.Loop((time) => { audioTest(); }, "8n").start(0); @@ -70,4 +34,3 @@ loop.probability = 0.8; transportStart.onclick = () => { Tone.Transport.start(); } - diff --git a/index.html b/index.html index bb9cd11..09e22de 100644 --- a/index.html +++ b/index.html @@ -112,15 +112,17 @@ + - - - - + + + + + \ No newline at end of file diff --git a/js/harmonyUtil.js b/js/harmonyUtil.js deleted file mode 100644 index f433593..0000000 --- a/js/harmonyUtil.js +++ /dev/null @@ -1,64 +0,0 @@ -import { extractPitchset } from "./toneGeneration.js"; - -// interval definitions: -const intervals = { - 0: "unison", - 1: "minor second", - 2: "major second", - 3: "minor third", - 4: "major third", - 5: "perfect fourth", - 6: "tritone" - // all intervals beyond this invert to one of the previous intervals -} - -// helper functions -const transposePitches = (pitchNames, interval) => { - let transposed = []; - pitchNames.forEach(pitch => transposed.push((pitch + interval) % 12)); - return transposed; -} - -const findVector = (pitches) => { - let sorted = pitches.sort((x,y) => x - y); - // sorted = sorted.filter((num, idx) => { - // return sorted.indexOf(num) === idx; - // }); - - // finds each interval and logs it as a duple - let intervalClasses = []; - for (let i = 0; i < sorted.length; i++) { - let j = i+1; - - // does not allow out of range values in the proceeding loop - if (j >= sorted.length) { - break; - } - - do { - let thing = (sorted[j] - sorted[i]) % 6 - if (!(intervalClasses.includes(thing))) { - intervalClasses.push(thing); - } - j++; - } while (j < sorted.length); - } - - intervalClasses = intervalClasses.sort((x,y) => x-y); - return intervalClasses; -} - -// analysis -let dMajor = extractPitchset(["D", "F#", "A", "D"]); -const eMajor = transposePitches(dMajor, 2); -console.log(eMajor); -console.log(''); - -let dMajVector = findVector(dMajor); -console.log(dMajVector); - -let complexVector = findVector([0,3,4,7,8,11]); -console.log(complexVector); - -let splitThird = findVector([0,3,4,7]); -console.log(splitThird); diff --git a/js/toneGeneration.js b/js/toneGeneration.js deleted file mode 100644 index 9787eaf..0000000 --- a/js/toneGeneration.js +++ /dev/null @@ -1,35 +0,0 @@ -// we start with a selection of pitches that generally work okay together -export const sopranoTones = ["B5", "A5", "G5", "F#5", "F5", "E5", "D5", "C5", "B4", "Bb4", "A4", "G4", "F#4", "F4", "E4"]; -export const altoTones = ["E5", "D5", "C5", "B4", "Bb4", "A4", "G4", "F#4", "F4", "E4", "D4", "C4", "B3", "Bb3", "A3", "G3"]; -export const tenorTones = ["G4", "F#4", "F4", "E4", "D4", "C4", "B3", "Bb3", "A3", "G3", "F3", "E3", "D3", "C3"]; -export const bassTones = ["C2", "D2", "E2", "F2", "G2", "A2", "Bb2", "B2", "C3", "D3", "E3", "F3", "G3"]; - -// now we define some rules to allow for the program to follow so it can some basic tenets of music theory -// we're going to include all pitches, so that it can use semitone-based pitch logic. -// this is focused on base-12, something computers understand quite well -const musicalPitches = ['A', "Bb", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]; - -export const extractPitchset = (pitches) => { - // 1) determine pitch set from given array of pitches - let pitchset = []; - - for (let each of pitches) { - // filters numbers from above tones - const str = each; - const regex = /[0-9]/g; - const withoutNums = str.replace(regex, ''); - const pitchNumber = musicalPitches.indexOf(withoutNums); - - // ... so that they may be mapped onto numbers corresponding to the chromatic scale - pitchset.push(pitchNumber); - } - - // these are sorted from lowest to highest index (something like an interval vector) - pitchset.sort((a,b) => a < b); - console.log(pitchset); - - return pitchset; -} - -// no tritones -// no minor 2nds or major 7ths \ No newline at end of file diff --git a/src/audioUtil.js b/src/audioUtil.js new file mode 100644 index 0000000..39c74c4 --- /dev/null +++ b/src/audioUtil.js @@ -0,0 +1,21 @@ +import { soundChord } from "../app.js"; +import { getRandomPitches } from "./vector_logic/getRandomPitches.js"; +import { extractPitchset } from "./vector_logic/extractPitchset.js"; +import { getProceduralPitches } from "./vector_logic/getProceduralPitches.js"; + +// initial test: generate a single, random chord +export const fullRandomChord = () => { + let pitches = getRandomPitches(); + soundChord(pitches); + let pitchset = extractPitchset(pitches); + + return pitchset; +} + +export const evaluatedChord = (prevPitches = ["C3", "G3", "C4", "G4"]) => { + let pitches = getProceduralPitches(prevPitches); + soundChord(pitches); + let pitchset = extractPitchset(pitches); + + return pitchset; +} \ No newline at end of file diff --git a/src/harmonyUtil.js b/src/harmonyUtil.js new file mode 100644 index 0000000..696320b --- /dev/null +++ b/src/harmonyUtil.js @@ -0,0 +1,27 @@ +export const sopranoTones = ["B5", "A5", "G5", "F#5", "F5", "E5", "D5", "C5", "B4", "Bb4", "A4", "G4", "F#4", "F4", "E4"]; +export const altoTones = ["E5", "D5", "C5", "B4", "Bb4", "A4", "G4", "F#4", "F4", "E4", "D4", "C4", "B3", "Bb3", "A3", "G3"]; +export const tenorTones = ["G4", "F#4", "F4", "E4", "D4", "C4", "B3", "Bb3", "A3", "G3", "F3", "E3", "D3", "C3"]; +export const bassTones = ["C2", "D2", "E2", "F2", "G2", "A2", "Bb2", "B2", "C3", "D3", "E3", "F3", "G3"]; + +export const pitchsets = [bassTones, tenorTones, altoTones, sopranoTones]; + +export const musicalPitches = ['A', "Bb", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]; + +// interval definitions: +export const intervalNames = { + 0: "unison", + 1: "minor second", + 2: "major second", + 3: "minor third", + 4: "major third", + 5: "perfect fourth", + 6: "tritone" + // all intervals beyond this invert to one of the previous intervals +} + +// helper functions +const transposePitches = (pitchNames, interval) => { + let transposed = []; + pitchNames.forEach(pitch => transposed.push((pitch + interval) % 12)); + return transposed; +} diff --git a/js/inputHandling.js b/src/inputHandling.js similarity index 90% rename from js/inputHandling.js rename to src/inputHandling.js index cb4ff7f..d2dfaff 100644 --- a/js/inputHandling.js +++ b/src/inputHandling.js @@ -1,4 +1,5 @@ -import { audioTest, fullRandomChord } from '../app.js'; +import { audioTest } from '../app.js'; +import { fullRandomChord, evaluatedChord } from './audioUtil.js'; // slider variables referring to DOM export const sopranoVol = document.getElementById('soprano-vol'); @@ -64,3 +65,6 @@ synthButton.onclick = audioTest; const randChord = document.getElementById('rand-chord'); randChord.onclick = fullRandomChord; + +const evalChord = document.getElementById('eval-chord'); +evalChord.onclick = evaluatedChord; diff --git a/js/styleUtils.js b/src/styleUtils.js similarity index 100% rename from js/styleUtils.js rename to src/styleUtils.js diff --git a/src/vector_logic/evaluateVector.js b/src/vector_logic/evaluateVector.js new file mode 100644 index 0000000..db32ff6 --- /dev/null +++ b/src/vector_logic/evaluateVector.js @@ -0,0 +1,21 @@ +import { getRandomPitches } from './getRandomPitches.js'; +import { extractPitchset } from "./extractPitchset.js"; +import { findVector } from "./findVector.js"; + +export const evaluateVector = (vector) => { + return ((vector.includes(1) || vector.includes(6))); +} + +export const rejectDissonance = (pitchset) => { + const vector = findVector(pitchset); + + // returns the pitchset and its vector if evaluateVector returns true, + if (evaluateVector(vector)) return vector; + + // and recursively calls the function otherwise. + if (!evaluateVector(vector)) { + let newPitches = getRandomPitches(); + let newPitchset = extractPitchset(newPitches); + rejectDissonance(newPitchset); + }; +} \ No newline at end of file diff --git a/src/vector_logic/extractPitchName.js b/src/vector_logic/extractPitchName.js new file mode 100644 index 0000000..016d63a --- /dev/null +++ b/src/vector_logic/extractPitchName.js @@ -0,0 +1,2 @@ +const regex = /[A-Gb#]/g; +export const extractPitchName = (tonePitchName) => tonePitchName.match(regex).join(''); diff --git a/src/vector_logic/extractPitchset.js b/src/vector_logic/extractPitchset.js new file mode 100644 index 0000000..62c9b1e --- /dev/null +++ b/src/vector_logic/extractPitchset.js @@ -0,0 +1,22 @@ +import { musicalPitches } from '../harmonyUtil.js'; +import { extractPitchName } from './extractPitchName.js'; + +// converts pitches in Tone.js string format to base-12 number pitchsets +export const extractPitchset = (pitches) => { + // 1) determine pitch set from given array of pitches + let pitchset = []; + + for (let each of pitches) { + // filters numbers from above tones + const str = each; + const withoutNums = extractPitchName(str); + const pitchNumber = musicalPitches.indexOf(withoutNums); + + // ... so that they may be mapped onto numbers corresponding to the chromatic scale + pitchset.push(pitchNumber); + } + + // these are sorted from lowest to highest index (something like an interval vector) + pitchset.sort((a,b) => a < b); + return pitchset; +} \ No newline at end of file diff --git a/src/vector_logic/findVector.js b/src/vector_logic/findVector.js new file mode 100644 index 0000000..2457b00 --- /dev/null +++ b/src/vector_logic/findVector.js @@ -0,0 +1,26 @@ +// takes pitches as numbers and converts them to a vector +export const findVector = (pitches) => { + let sorted = pitches.sort((x,y) => x - y); + + // finds each interval and logs it as a duple + let intervalClasses = []; + for (let i = 0; i < sorted.length; i++) { + let j = i+1; + + // does not allow out of range values in the proceeding loop + if (j >= sorted.length) { + break; + } + + do { + let thing = (sorted[j] - sorted[i]) % 6 + if (!(intervalClasses.includes(thing))) { + intervalClasses.push(thing); + } + j++; + } while (j < sorted.length); + } + + intervalClasses = intervalClasses.sort((x,y) => x-y); + return intervalClasses; +} diff --git a/src/vector_logic/getProceduralPitches.js b/src/vector_logic/getProceduralPitches.js new file mode 100644 index 0000000..bd9c5e6 --- /dev/null +++ b/src/vector_logic/getProceduralPitches.js @@ -0,0 +1,54 @@ +import { pitchsets, musicalPitches } from "../harmonyUtil.js"; +import { extractPitchName } from "./extractPitchName.js"; +import { getRandomIndex } from "./getRandomPitches.js"; + +// an additional method based on the structure of getRandomPitches, +// but taking some principles of music theory into account. +export const getProceduralPitches = (prevPitches) => { + // prevPitches is passed in to ensure there is no linear dissonance within voices + let pitches = []; + let formattedPitches = []; + + for (let voice of pitchsets) { + let index; + let formattedPitch; + + while (pitches.length <= pitchsets.indexOf(voice)) { + // the first section of this while loop is more free. + index = getRandomIndex(voice); + formattedPitch = extractPitchName(voice[index]); + + // this initial condition will apply only to the bass voice, + if (!pitches.length) { + pitches.push(voice[index]); + } else { + // now we need some repeating logic for the remaining four voices + while (pitches.length !== (pitchsets.indexOf(voice) + 1)) { + index = getRandomIndex(voice); + pitches.push(voice[index]); + + + for (let i = 0; i < pitches.length; i++) { + let numVals = []; + + for (let j = i; j < i + 2; j++) { + console.log(pitches[j]); + let extracted = extractPitchName(pitches[j]); + numVals.push(musicalPitches.indexOf(extracted) + 1); + } + + let difference = Math.abs(numVals.reduce((x,y) => x - y)); + if (difference === 1 || difference === 6) { + pitches.pop(); + } else { + continue; + } + } + } + } + } + } + + console.log(pitches); + return pitches; +} diff --git a/src/vector_logic/getRandomPitches.js b/src/vector_logic/getRandomPitches.js new file mode 100644 index 0000000..fb31de6 --- /dev/null +++ b/src/vector_logic/getRandomPitches.js @@ -0,0 +1,32 @@ +import { pitchsets } from "../harmonyUtil.js"; +import { extractPitchName } from "./extractPitchName.js"; + +// helper function for assigning a random index for a given voice's pitchset +export const getRandomIndex = (voice) => Math.floor(Math.random() * 100) % voice.length; + +export const getRandomPitches = () => { + // pitches stored in Tone.js string format + let pitches = []; + let formattedPitches = []; + + for (let voice of pitchsets) { + // finds a random index, excluding any which may already exist in the array + let index; + let formattedPitch; + + // loops until four distinct chord members are received + while (formattedPitches.length <= pitchsets.indexOf(voice)) { + index = getRandomIndex(voice); + + formattedPitch = extractPitchName(voice[index]); + if (!formattedPitches.includes(formattedPitch)) { + formattedPitches.push(formattedPitch); + pitches.push(voice[index]); + } + } + } + + console.log(formattedPitches); + console.log(pitches); + return pitches; +} diff --git a/src/vector_logic/helper.js b/src/vector_logic/helper.js new file mode 100644 index 0000000..77fd2d9 --- /dev/null +++ b/src/vector_logic/helper.js @@ -0,0 +1,80 @@ +import { pitchsets, musicalPitches } from '../harmonyUtil.js'; +import { extractPitchName } from './extractPitchName.js'; +import { getRandomIndex } from './getRandomPitches.js'; + +const iteratePitchsets = () => { + let pitches = []; + let result = []; + + const tryNewPitch = () => { + result = []; + + for (let voice of pitchsets) { + let idx = getRandomIndex(voice); + let pitchNum = musicalPitches.indexOf(extractPitchName(voice[idx])); + + pitches.push([pitchNum, voice[idx]]); + } + + console.log(pitches); + + for (let i = 0; i < pitches.length; i++) { + for (let j = i; j < pitches.length; j++) { + let difference = Math.abs(pitches[i][0] - pitches[j][0]); + if (difference === 1 || difference === 6) { + result.push(["BAD", [i, j], pitches[i], pitches[j]]); + } else { + result.push(["GOOD", [i, j], pitches[i], pitches[j]]); + } + } + } + console.log(result); + } + + tryNewPitch(); + + for (let entry in result) { + if (entry[0] === "BAD") { + iteratePitchsets(); + } + } + + console.log(pitches); +} + +iteratePitchsets(); + + + +function twoPointIteration() { + let caught = false; + let data = [1,2,3,4,5,6]; + + for (let i = 0; i < data.length; i++) { + console.log(data[i]); + for (let j = 0; j < data.length; j++) { + if (data[i] === data[j]) continue; + + let difference = Math.abs(data[j] - data[i]); + + if (difference === 3) { + difference = "caught: 3"; + } + + console.log([difference, [data[i], data[j]]]); + } + + console.log('next'); + } + + return caught; +} + +function selfReferentialPointer() { + for (let i = 0; i < 5; i++) { + console.log(`i: ${i}`); + for (let j = i; j < i+2; j++) { + console.log(`jjjjjjj: ${j % 3}`); + } + } +} diff --git a/src/vector_logic/numbersToPitches.js b/src/vector_logic/numbersToPitches.js new file mode 100644 index 0000000..e69de29