"use strict"; // // Voicegardens front-end Javascript // var archiveUrl = window.location + "add-to-archive"; var canvasHeight; var canvasWidth; var centerX; var centerY; var frameRate = 30; var microphone; var newSoundJustRecorded = false; var playButton; var recordButton; var recorder; var recording; var recordingTimeout = 30000; // 30 seconds (in milliseconds) var screenX; var screenY; var secondTick = false; var shapes = []; var stopButton; var timer = 0; var toScreenX; var toScreenY; function record() { /** * Starting recording. **/ if (microphone.enabled) { setTimeout(recorder.record(recording), recordingTimeout); } } function stop() { /** * Stop recording a new recording. **/ if (recorder.recording) { recorder.stop(); newSoundJustRecorded = true; } } function play() { /** * Play the recording. **/ if (recording.isLoaded()) { recording.play(); } } function archive() { /** * Send the recording to the back-end. **/ var soundBlob = recording.getBlob(); var formData = new FormData(); var date = new Date(); var filename = date.getTime().toString() + ".wav"; formData.append("file", soundBlob, filename); var config = new Headers({ "Content-Type": "multipart/form-data" }); axios.post(archiveUrl, formData, config).catch(function(error) { console.log( "Upload failed!", "Received the following message:", error.response.data ); }); } function showArchive() { window.location.href = "/archive"; } function setupRecording() { /** * Setup logic for recording. **/ microphone = new p5.AudioIn(); microphone.start(); recorder = new p5.SoundRecorder(); recorder.setInput(microphone); recording = new p5.SoundFile(); recordButton = createButton("record"); recordButton.position(10, 5); recordButton.mousePressed(record); stopButton = createButton("stop"); stopButton.position(10, 40); stopButton.mousePressed(stop); playButton = createButton("play"); playButton.position(10, 75); playButton.mousePressed(play); playButton = createButton("archive"); playButton.position(10, 110); playButton.mousePressed(archive); playButton = createButton("view archive"); playButton.position(10, 145); playButton.mousePressed(showArchive); } function getSoundInfo() { /** * Retrieve sound information like pitch, amplitude, duration, etc. **/ amplitude = recording.getPeaks(); duration = recording.duration(); // pitch (frequency?) I think we can use fft.analyze() and then find the // highest value (0 -> 1024) that has a non-zero value this gives us the // highest frequency from the recording // https://p5js.org/reference/#/p5.FFT // https://p5js.org/reference/#/p5.FFT/analyze // nuance? // "I meant the amount of variation in the voice - i.e is it one single // monotone note or does it go up and down octaves or start soft and high and // become deep and guttural etc." // // How do to do this? Unsure ... } class GeneratedShape { constructor() { /** * Initialise the new shape. **/ this.synth = new p5.MonoSynth(); this.opacity = 0; this.colour = this.chooseColour(); this.accelX = 0.0; this.accelY = 0.0; this.deltaX = 0.0; this.deltaY = 0.0; this.springing = 0.0009; this.damping = 0.98; this.organicConstant = random(-5, 5); this.startXs = []; this.startYs = []; this.xs = []; this.ys = []; this.angle = []; this.frequency = []; this.edges = 5; this.radius = random(120, 140); this.rotAngle = radians(360 / this.edges); this.randomX = random(-77, 77); this.randomY = random(-77, 77); this.destX = random(canvasWidth); this.destY = random(canvasHeight); this.centerX = random(windowWidth); this.centerY = random(windowHeight); this.initialise(); } initialise() { /** * Initialise all movement related arrays. **/ for (let i = 0; i < this.edges; i++) { this.startXs[i] = 0; this.startYs[i] = 0; this.ys[i] = 0; this.xs[i] = 0; this.angle[i] = 0; } for (let i = 0; i < this.edges; i++) { this.frequency[i] = random(5, 12); } } collide(shapes) { /** * Detect if the shape collides with another shape. **/ // TODO: implement once again return false; } sound() { /** * Play a sound after a collision is detected. **/ // TODO: implement once again return; } fadein() { /** * Fade-in the shape using alpha values. **/ if (this.opacity < 256) { let currentAlpha = this.colour._getAlpha(); this.colour.setAlpha(currentAlpha + random(0, 3)); } else { this.opacity = 256; } } chooseColour() { /** * Choose a colour for the shape. **/ let colourChoices = [ color("red"), color("blue"), color("green"), color("black"), color("white") ]; let index = floor(random(0, colourChoices.length)); let chosenColour = colourChoices[index]; chosenColour.setAlpha(this.opacity); return chosenColour; } draw() { /** * Draw the shape vectors. **/ if (this.opacity != 256) this.fadein(); fill(this.colour); for (let i = 0; i < this.edges; i++) { this.startXs[i] = this.centerX + cos(radians(this.rotAngle) * i) * this.radius + this.randomX; this.startYs[i] = this.centerY + sin(radians(this.rotAngle) * i) * this.radius + this.randomY; this.rotAngle += 360 / this.edges; } curveTightness(this.organicConstant); beginShape(); for (let i = 0; i < this.edges; i++) { curveVertex(this.xs[i], this.ys[i]); } endShape(CLOSE); } move() { /** * Move the shape vectors. **/ this.deltaX = mouseX - this.centerX; this.deltaY = mouseY - this.centerY; this.deltaX *= this.springing; this.deltaY *= this.springing; this.accelX += this.deltaX; this.accelY += this.deltaY; this.centerX += this.accelX; this.centerY += this.accelY; this.accelX *= this.damping; this.accelY *= this.damping; this.organicConstant = 1 - (abs(this.accelX) + abs(this.accelY)) * 0.1; for (let i = 0; i < this.edges; i++) { this.xs[i] = this.startXs[i] + sin(radians(this.angle[i])) * (this.accelX * 2); this.ys[i] = this.startYs[i] + sin(radians(this.angle[i])) * (this.accelY * 2); this.angle[i] += this.frequency[i]; } } } function setup() { /** * The p5.js initial setup function. **/ canvasWidth = windowWidth; canvasHeight = windowHeight; createCanvas(canvasWidth, canvasHeight); frameRate(frameRate); setupRecording(); centerX = canvasWidth / 2; centerY = canvasHeight / 2; screenX = toScreenX = 0; screenY = toScreenY = 0; } function draw() { /** * The p5.js draw loop. **/ background("#69D2E7"); blendMode(BLEND); smooth(); noStroke(); screenX = lerp(screenX, toScreenX, 0.2); screenY = lerp(screenY, toScreenY, 0.2); translate(screenX, screenY); if (newSoundJustRecorded === true) { shapes.push(new GeneratedShape()); newSoundJustRecorded = false; } if (millis() >= 1000 + timer) { secondTick = true; timer = millis(); } for (let i = 0; i < shapes.length; i++) { let shape = shapes[i]; shape.draw(); shape.move(); if (secondTick) { setTimeout(function() { shape.destX = random(canvasWidth); shape.destY = random(canvasHeight); }, random(100, 3000)); } if (shape.collide(shapes) === true) { shape.sound(); } } secondTick = false; } function mouseDragged() { /** * Mouse drag movement handling. **/ toScreenX += mouseX - pmouseX; toScreenY += mouseY - pmouseY; } function windowResized() { /** * Canvas re-draw handling. **/ resizeCanvas(windowWidth, windowHeight); }