Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robstenzinger/4f64ab4de7d52edaa1204130936803f8 to your computer and use it in GitHub Desktop.
Save robstenzinger/4f64ab4de7d52edaa1204130936803f8 to your computer and use it in GitHub Desktop.
Generating Music Based on Text and Seeded Randomality

Generating Music Based on Text and Seeded Randomality

Generate music and a visualization based on the text length and sentiment + the given list of rhythms and note patterns + seeded randomness.

This experiment uses:

  • Gibber.js as a synth and sequencer
  • AFINN sentiment analysis to help with generating the music
  • Seeded randomness to be able to generate the same music given the same input
  • P5js to for the UI and visualization of the sound

A Pen by Rob Stenzinger on CodePen.

License.

<!--
Generate music and a visualization
based on the text length and sentiment +
the given curated list of rhythms and
note patterns + seeded randomality
---
This experiment uses so many awesome
components, see the "settings" for
more details about:
Gibber.js as a synth
and sequencer
AFINN sentiment analysis
to help with generating the music
Seeded randomality to be able
to generate the same music
given the same input
P5js to for the UI and
visualization of the sound
-->
// sets which AFINN language the sentiment parser will use
var afinn = afinn_en;
//
// globals and config for the music and gibber.js
//
var drums;
var followDrums;
var followBass;
var fftSize = Math.pow(2,6);
var fft;
var randomInstrument;
var instrumentFM;
var bass;
var randomSynth;
// note: gibber drums:
// x : kick drum hit
// o : snare drum hit
// * : close hihat
// - : open hihat
// . : rest
var drumPatterns = ["x.x.x.xx", "x*o*x*o-","xxo.xoxx.","xxoxxoxx.xx-"];
var drumRhythms = [1/4,1/8,1/16];
var noteSequences = [[0,0,0,7,6,12,14],[0],[0,0,0,7,14],[5,7,1]];
var noteRhythms = [1/2,1,1/4,1/8,1/16];
var instruments = ["FM", "Pluck", "Synth2"];
var voicesFM = ["bass","drum2"];
var voicesSynth = ["bleep","rhodes"];
var bpm = 0;
var totalInstruments = 0;
var followList = [];
var instrumentOptions = [];
var input;
var instrumentList = [];
var moodCircles = [];
var tweetText = "This looks like something I might tweet!";
// randomness utilities
function randomRange(min, max) {
return ((Math.random()*(max-min)) + min);
}
function randomFloor(min, max) {
return Math.floor(randomRange(min, max));
}
function randomChoice(array){
var choice = array[randomFloor(0,array.length)];
// console.log("chose: ", choice);
return choice;
}
function randomSequence(array, length){
// console.log("randomSequence");
var choices = [];
for(i = 0; i<length; i++){
choices.push(randomChoice(array));
}
}
// sentiment utilities
function sentiment_to_bpm(score){
var minimum_tempo = 50;
var strength_multiplier = 16;
return Math.round(Math.abs(score*strength_multiplier) + minimum_tempo);
}
function text_length_to_number_instruments(length){
var minimum_instruments = 1;
var per_instrument_count = 35;
return Math.round(minimum_instruments + (length / per_instrument_count));
}
//
// p5 setup
function setup() {
// p5 visual canvas
createCanvas(windowWidth, windowHeight);
colorMode(HSB, 255);
background(0,0,0);
input = createInput();
input.value(tweetText);
input.position((windowWidth * .10), windowHeight * .90);
// fill(100,100,100);
textSize(60);
textFont("Helvetica");
strokeWeight(1);
text("generate music with text:", (windowWidth * .10), windowHeight * .10);
text("enter text, click the generate button", (windowWidth * .10), windowHeight * .30);
text("perform live: change the text to", (windowWidth * .10), windowHeight * .50);
text("something new and back again", (windowWidth * .10), windowHeight * .70);
var generateMusicButton = createButton("generate music using this text");
generateMusicButton.position(input.x + input.width, input.y);
generateMusicButton.mousePressed(createMusic);
var stopMusicButton = createButton("stop");
stopMusicButton.position(generateMusicButton.x + generateMusicButton.width, generateMusicButton.y);
stopMusicButton.mousePressed(clearMusic);
Clock.bpm = 0;
}
// p5 draw
function draw() {
if(Clock.bpm > 0){
// visual feedback: background based on drums
background(followDrums.getValue() * 210, followDrums.getValue() * 210, followDrums.getValue() * 210);
// more visual feedback
moodCircles.forEach(function(mc, index, array){
mc.update(index);
});
}
}
//
// visualization via mood circle
//
function MoodCircle(x,y,w,h, follow){
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.hsv_v = randomRange(0,255);
this.update = function(index){
//get an fft ratio
var fftValue = fft[index]/255;
//update values
this.width = fftValue * width;
this.height = fftValue * height;
this.x = fftValue * x;
this.y = fftValue * y;
//display results
fill(follow.getValue() * 255, 255, this.hsv_v);
// ellipse(this.x, this.y, this.width, this.height);
rect(this.x, this.y, this.width, this.height);
};
}
//
// music management
//
function clearMusic(){
Clock.bpm = 0;
Gibber.clear();
instrumentList = [];
instrumentOptions = [];
moodCircles = [];
}
function createMusic(){
clearMusic();
// get the text (would be cool to someday feed this via tweet)
console.log("input", input);
console.log("input.value()", input.value())
var inputValue = input.value();
if(inputValue){
tweetText = inputValue;
}
// prepare the seeded randomality
Math.seedrandom(tweetText);
// gather sentiment
sentiment_result = sentiment(tweetText);
console.log("sentiment: ", sentiment_result);
console.log("sentiment.score: ", sentiment_result.score)
// text length to number of instruments
totalInstruments = text_length_to_number_instruments(tweetText.length);
console.log("totalInstruments", totalInstruments);
// todo: sentiment chooses which pool of drum beats
commonRhythm = randomChoice(drumRhythms);
console.log("commonRhythm: ", commonRhythm);
// todo: sentiment chooses which pool of rhythms to choose from
alternateRhythm = randomChoice(noteRhythms);
console.log("alternateRhythm: ", alternateRhythm);
// random chooses the note patterns
commonNotePattern = randomChoice(noteSequences);
console.log("commonNotePattern: ", commonNotePattern);
bpm = sentiment_to_bpm(sentiment_result.score);
Clock.bpm = bpm;
console.log("beats per minute: ", bpm);
instrumentOptions = [];
function goDrums(){
drums = EDrums(randomChoice(drumPatterns), commonRhythm);
drumsAmp = randomRange(0.30, 0.80);
followDrums = Follow(drums);
followList.push(followDrums);
drums.fadeIn(1, drumsAmp);
instrumentList.push(drums);
}
instrumentOptions.push(goDrums);
function goFM(){
chosenVoice = randomChoice(voicesFM);
console.log("FM voice melody bass: ", chosenVoice);
instrumentFM = FM(chosenVoice)
.note.seq(commonNotePattern, commonRhythm )
instrumentFM.amp = randomRange(0.20, 0.80);
instrumentList.push(instrumentFM);
}
instrumentOptions.push(goFM);
function goInstrument(){
chosenInstrument = randomChoice(instruments);
console.log("TBD instrument voice bass: ", chosenInstrument);
randomInstrument = window[chosenInstrument]()
.note.seq(commonNotePattern, alternateRhythm )
randomInstrumentAmp = randomRange(0.10, 0.60);
randomInstrument.fadeIn(2, randomInstrumentAmp);
instrumentList.push(randomInstrument);
}
instrumentOptions.push(goInstrument);
function goSynth(){
chosenSynth = randomChoice(voicesSynth);
console.log("Synth: ", chosenSynth);
randomSynth = Synth(chosenSynth, {amp:.35} )
.chord.seq(Rndi(0,6,3), 1 )
.fx.add(Delay());
instrumentList.push(randomSynth);
}
instrumentOptions.push(goSynth);
function goFM2(){
chosenVoice = randomChoice(voicesFM);
console.log("FM voice bass: ", chosenVoice);
bass = FM(chosenVoice)
.note.seq(commonNotePattern, commonRhythm);
bassAmp = randomRange(0.20, 0.80);
bass.fadeIn(2, bassAmp);
followBass = Follow(bass);
followList.push(followBass);
instrumentList.push(bass);
}
instrumentOptions.push(goFM2);
follow_main = Follow(Gibber.Master, 1024);
followList.push(follow_main);
fft = FFT(fftSize);
var minimumCircleWidth = 50;
var minInstruments = 1;
var maxInstruments = instrumentOptions.length;
if(totalInstruments > maxInstruments){
totalInstruments = maxInstruments-1
}else if(totalInstruments < minInstruments){
totalInstruments = minInstruments;
}
for(var i=0; i<totalInstruments; i++){
// activate an available instrument
instrumentOptions[i]();
var x = randomRange(0,windowWidth*.4);
var y = randomRange(0,windowHeight*.5);
var width = randomRange(minimumCircleWidth, windowWidth*.10);
var height = width;
var follow = randomChoice(followList);
moodCircles.push(new MoodCircle(x,y,width,height,follow));
}
}
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/498726/p5.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/498726/p5.dom.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/498726/p5.gibber.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/498726/seedrandom.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/498726/afinn_en.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/498726/sentiment.js"></script>
html, body{
margin:0;
padding:0;
}
input{
width: 30%;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment