|
// 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)); |
|
} |
|
|
|
} |
|
|