Last active
December 23, 2021 02:41
-
-
Save koolamusic/b6eea69382d5217165e28176f07a2be7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>Tonejs Demo</title> | |
</head> | |
<body> | |
<!-- Learn music theory https://www.youtube.com/watch?v=rgaTLrZGlk0 --> | |
<!-- Using Tonejs https://www.devbridge.com/articles/tonejs-coding-music-production-guide/ | |
--> | |
<script src="https://unpkg.com/tone"></script> | |
<button id="play-button">Play/Pause</button> | |
<script> | |
const AMinorScale = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; | |
const addOctaveNumbers = (scale, octaveNumber) => scale.map(note => { | |
const firstOctaveNoteIndex = scale.indexOf('C') !== -1 ? scale.indexOf('C') : scale.indexOf('C#') | |
const noteOctaveNumber = scale.indexOf(note) < firstOctaveNoteIndex ? octaveNumber - 1 : octaveNumber; | |
return `${note}${noteOctaveNumber}` | |
}); | |
const constructMajorChord = (scale, octave, rootNote) => { | |
const scaleWithOctave = addOctaveNumbers(scale, octave); | |
const getNextChordNote = (note, nextNoteNumber) => { | |
const nextNoteInScaleIndex = scaleWithOctave.indexOf(note) + nextNoteNumber - 1; | |
let nextNote; | |
if (typeof(scaleWithOctave[nextNoteInScaleIndex]) !== 'undefined') { | |
nextNote = scaleWithOctave[nextNoteInScaleIndex]; | |
} else { | |
nextNote = scaleWithOctave[nextNoteInScaleIndex - 7]; | |
const updatedOctave = parseInt(nextNote.slice(1)) + 1; | |
nextNote = `${nextNote.slice(0,1)}${updatedOctave}`; | |
} | |
return nextNote; | |
} | |
const thirdNote = getNextChordNote(rootNote, 3) | |
const fifthNote = getNextChordNote(rootNote, 5) | |
const chord = [rootNote, thirdNote, fifthNote] | |
return chord | |
} | |
const constructChords = (scale, octave) => { | |
const scaleWithOctave = addOctaveNumbers(scale, octave); | |
const getNextChordNote = (note, nextNoteNumber) => { | |
const nextNoteInScaleIndex = scaleWithOctave.indexOf(note) + nextNoteNumber - 1; | |
let nextNote; | |
if (typeof(scaleWithOctave[nextNoteInScaleIndex]) !== 'undefined') { | |
nextNote = scaleWithOctave[nextNoteInScaleIndex]; | |
} else { | |
nextNote = scaleWithOctave[nextNoteInScaleIndex - 6]; | |
const updatedOctave = parseInt(nextNote.slice(1)) + 1; | |
nextNote = `${nextNote.slice(0,1)}${updatedOctave}`; | |
} | |
return nextNote; | |
} | |
const chordArray = scaleWithOctave.map(note => { | |
let thirdNote = getNextChordNote(note, 3) | |
let fifthNote = getNextChordNote(note, 5) | |
const chord = [note, thirdNote, fifthNote] | |
return chord | |
}) | |
return chordArray; | |
} | |
Tone.Transport.bpm.value = 150 | |
const IChord = constructMajorChord(AMinorScale, 4, 'A3'); | |
const VChord = constructMajorChord(AMinorScale, 4, 'E4'); | |
const VIChord = constructMajorChord(AMinorScale, 3, 'F3'); | |
const IVChord = constructMajorChord(AMinorScale, 3, 'D3'); | |
IChord.push('A2', 'G4') | |
VChord.push('E2', 'G3') | |
VIChord.push('F2', 'E4') | |
IVChord.push('D2', 'C4') | |
console.log(IChord); | |
console.log(VChord); | |
console.log(VIChord); | |
console.log(IVChord); | |
const synth = new Tone.PolySynth(Tone.Synth, { | |
maxPolyphony: 5, | |
volume: -5, | |
oscillator : { | |
type : "sawtooth" | |
} | |
}).toDestination(); | |
const mainChords = [ | |
{'time': 0, 'note': IChord, 'duration': '2n.'}, | |
{'time': '0:3', 'note': VChord, 'duration': '4n'}, | |
{'time': '1:0', 'note': VIChord, 'duration': '2n.'}, | |
{'time': '1:3', 'note': VChord, 'duration': '4n'}, | |
{'time': '2:0', 'note': IVChord, 'duration': '2n.'}, | |
{'time': '2:3', 'note': VChord, 'duration': '4n'}, | |
{'time': '3:0', 'note': VIChord, 'duration': '2n'}, | |
{'time': '3:2', 'note': VChord, 'duration': '4n'}, | |
{'time': '3:3', 'note': IVChord, 'duration': '4n'}, | |
{'time': '4:0', 'note': IChord, 'duration': '2n.'}, | |
{'time': '4:3', 'note': VChord, 'duration': '4n'}, | |
{'time': '5:0', 'note': VIChord, 'duration': '2n.'}, | |
{'time': '5:3', 'note': VChord, 'duration': '4n'}, | |
{'time': '6:0', 'note': IVChord, 'duration': '2n.'}, | |
{'time': '6:3', 'note': VChord, 'duration': '4n'}, | |
{'time': '7:0', 'note': VIChord, 'duration': '2n'}, | |
{'time': '7:2', 'note': VChord, 'duration': '4n'}, | |
{'time': '7:3', 'note': IVChord, 'duration': '4n'}, | |
]; | |
const part = new Tone.Part(function(time, note){ | |
synth.triggerAttackRelease(note.note, note.duration, time); | |
}, mainChords).start(0); | |
const IChord1 = constructMajorChord(AMinorScale, 5, 'A4'); | |
const VChord1 = constructMajorChord(AMinorScale, 5, 'E5'); | |
const VIChord1= constructMajorChord(AMinorScale, 4, 'F4'); | |
const IVChord1 = constructMajorChord(AMinorScale, 4, 'D4'); | |
IChord.push('A3', 'G5') | |
VChord.push('E3', 'D5') | |
VIChord.push('F3', 'E5') | |
IVChord.push('D3', 'C5') | |
const mainChordPart = new Tone.PolySynth(Tone.Synth, { | |
maxPolyphony: 5, | |
oscillator : { | |
count: 6, | |
spread: 80, | |
type : "fatsawtooth" | |
} | |
}).toDestination(); | |
const highOctaveChords = [ | |
{'time': 0, 'note': IChord1, 'duration': '2n.'}, | |
{'time': '0:3', 'note': VChord1, 'duration': '4n'}, | |
{'time': '1:0', 'note': VIChord1, 'duration': '2n.'}, | |
{'time': '1:3', 'note': VChord1, 'duration': '4n'}, | |
{'time': '2:0', 'note': IVChord1, 'duration': '2n.'}, | |
{'time': '2:3', 'note': VChord1, 'duration': '4n'}, | |
{'time': '3:0', 'note': VIChord1, 'duration': '2n'}, | |
{'time': '3:2', 'note': VChord1, 'duration': '4n'}, | |
{'time': '3:3', 'note': IVChord1, 'duration': '4n'}, | |
{'time': '4:0', 'note': IChord1, 'duration': '2n.'}, | |
{'time': '4:3', 'note': VChord1, 'duration': '4n'}, | |
{'time': '5:0', 'note': VIChord1, 'duration': '2n.'}, | |
{'time': '5:3', 'note': VChord1, 'duration': '4n'}, | |
{'time': '6:0', 'note': IVChord1, 'duration': '2n.'}, | |
{'time': '6:3', 'note': VChord1, 'duration': '4n'}, | |
{'time': '7:0', 'note': VIChord1, 'duration': '2n'}, | |
{'time': '7:2', 'note': VChord1, 'duration': '4n'}, | |
{'time': '7:3', 'note': IVChord1, 'duration': '4n'}, | |
]; | |
const highSynth = new Tone.PolySynth(Tone.Synth, { | |
volume: -16, | |
maxPolyphony: 5, | |
count: 6, | |
spread: 80, | |
oscillator : { | |
type : "fatsawtooth" | |
} | |
}).toDestination(); | |
const highOctaveChordPart = new Tone.Part(function(time, note){ | |
highSynth.triggerAttackRelease(note.note, note.duration, time, 0.5); | |
}, highOctaveChords).start(0); | |
const mainMelody = [ | |
{'time': 0, 'note': 'G4', 'duration': '8n'}, | |
{'time': '0:0:2', 'note': 'F4', 'duration': '8n'}, | |
{'time': '0:1', 'note': 'D4', 'duration': '8n.'}, | |
{'time': '0:2', 'note': 'D4', 'duration': '8n'}, | |
{'time': '0:2:2', 'note': 'F4', 'duration': '8n.'}, | |
{'time': '0:3', 'note': 'G4', 'duration': '8n'}, | |
{'time': '0:3:2', 'note': 'A4', 'duration': '2n'}, | |
{'time': '2:0', 'note': 'A4', 'duration': '8n'}, | |
{'time': '2:0:2', 'note': 'G4', 'duration': '8n'}, | |
{'time': '2:1', 'note': 'F4', 'duration': '8n'}, | |
{'time': '2:2', 'note': 'A4', 'duration': '8n'}, | |
{'time': '2:2:2', 'note': 'G4', 'duration': '8n'}, | |
{'time': '2:3', 'note': 'E4', 'duration': '8n'}, | |
{'time': '2:3:2', 'note': 'F4', 'duration': '2n'}, | |
{'time': '4:0', 'note': 'G4', 'duration': '8n'}, | |
{'time': '4:0:2', 'note': 'F4', 'duration': '8n'}, | |
{'time': '4:1', 'note': 'D4', 'duration': '8n'}, | |
{'time': '4:2', 'note': 'F4', 'duration': '8n'}, | |
{'time': '4:2:2', 'note': 'A4', 'duration': '8n'}, | |
{'time': '4:3', 'note': 'G4', 'duration': '8n'}, | |
{'time': '4:3:2', 'note': 'A4', 'duration': '2n'}, | |
{'time': '5:2:2', 'note': 'G4', 'duration': '8n'}, | |
{'time': '5:3', 'note': 'A4', 'duration': '8n'}, | |
{'time': '5:3:2', 'note': 'B4', 'duration': '8n'}, | |
{'time': '6:0', 'note': 'C5', 'duration': '8n'}, | |
{'time': '6:1', 'note': 'B4', 'duration': '8n'}, | |
{'time': '6:1:2', 'note': 'A4', 'duration': '8n'}, | |
{'time': '6:2', 'note': 'B4', 'duration': '8n'}, | |
{'time': '6:2:2', 'note': 'A4', 'duration': '8n'}, | |
{'time': '6:3', 'note': 'G4', 'duration': '8n'}, | |
{'time': '6:3:2', 'note': 'A4', 'duration': '1n'}, | |
]; | |
const synth2 = new Tone.Synth({ | |
oscillator : { | |
volume: 5, | |
count: 3, | |
spread: 40, | |
type : "fatsawtooth" | |
} | |
}).toDestination(); | |
const mainMelodyPart = new Tone.Part(function(time, note){ | |
synth2.triggerAttackRelease(note.note, note.duration, time); | |
}, mainMelody).start(0); | |
const lowPass = new Tone.Filter({ | |
frequency: 8000, | |
}).toDestination(); | |
const snareDrum = new Tone.NoiseSynth({ | |
noise: { | |
type: 'white', | |
playbackRate: 3, | |
}, | |
envelope: { | |
attack: 0.001, | |
decay: 0.20, | |
sustain: 0.15, | |
release: 0.03, | |
}, | |
}).connect(lowPass); | |
const snares = [ | |
{ time: '0:2' }, | |
{ time: '1:2' }, | |
{ time: '2:2' }, | |
{ time: '3:2' }, | |
{ time: '4:2' }, | |
{ time: '5:2' }, | |
{ time: '6:2' }, | |
{ time: '7:2' }, | |
] | |
const snarePart = new Tone.Part(function(time){ | |
snareDrum.triggerAttackRelease('4n', time) | |
}, snares).start(0); | |
const kickDrum = new Tone.MembraneSynth({ | |
volume: 6 | |
}).toDestination(); | |
const kicks = [ | |
{ time: '0:0' }, | |
{ time: '0:3:2' }, | |
{ time: '1:1' }, | |
{ time: '2:0' }, | |
{ time: '2:1:2' }, | |
{ time: '2:3:2' }, | |
{ time: '3:0:2' }, | |
{ time: '3:1:' }, | |
{ time: '4:0' }, | |
{ time: '4:3:2' }, | |
{ time: '5:1' }, | |
{ time: '6:0' }, | |
{ time: '6:1:2' }, | |
{ time: '6:3:2' }, | |
{ time: '7:0:2' }, | |
{ time: '7:1:' }, | |
]; | |
const kickPart = new Tone.Part(function(time){ | |
kickDrum.triggerAttackRelease('C1', '8n', time) | |
}, kicks).start(0); | |
const bassline = [ | |
{'time': 0, 'note': 'A0', 'duration': '2n'}, | |
{'time': '0:3', 'note': 'F0', 'duration': '2n.'}, | |
{'time': '1:3', 'note': 'D0', 'duration': '2n.'}, | |
{'time': '2:3', 'note': 'F0', 'duration': '1:1'}, | |
]; | |
const bass = new Tone.Synth({ | |
oscillator : { | |
type : "triangle" | |
} | |
}).toDestination(); | |
const bassPart = new Tone.Part(function(time, note){ | |
bass.triggerAttackRelease(note.note, note.duration, time); | |
}, bassline).start(0); | |
document.getElementById("play-button").addEventListener("click", function() { | |
if (Tone.Transport.state !== 'started') { | |
Tone.Transport.start(); | |
} else { | |
Tone.Transport.stop(); | |
} | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Use mediarecorder API to create audio recording from context, supported formats [webm, opus, wav] | |
/** @see https://tonejs.github.io/docs/14.7.77/Recorder */ | |
let ac = new AudioContext() | |
let audioStream = ac.createMediaStreamDestination().stream; | |
let recorder = new Recorder(audioStream) | |
ac.destination.connect(audioStream); | |
class Recorder { | |
constructor(audioStream){ | |
this._streams = audioStream.getAudioTracks()[0]) | |
this._chunks = [] | |
this.recorder = new MediaRecorder(this._streams) | |
this.recorder.ignoreMutedMedia = false | |
this.recorder.addEventListener('dataavailable', (e) => this.saveChunks(e)) | |
this.recorder.addEventListener('stop', (e) => this.downloadStream(e)) | |
this._mimeType = 'audio/wav' | |
} | |
saveChunks(e) { | |
e.data.size && this._chunks.push(e.data); | |
} | |
start(fileName = 'file'){ | |
this._fileName = fileName | |
this.recorder.start(); | |
} | |
stop(){ | |
setTimeout(() => this.recorder.stop(), 1000) | |
} | |
downloadStream(e) { | |
if (this._chunks.length) { | |
let blob = new Blob(this._chunks) | |
// download the blob to a file | |
// you can use this helper: http://danml.com/download.html | |
download(blob, this._fileName + '.wav', this._mimeType); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment