xxxxxxxxxx
433
let soundRecorder, soundFile;
let transitionData = [];
let currentPlaybackNoteIndex = 0
let markovProbabilities = {};
let noteNames = {
261.63: 'C4',
277.18: 'C#4',
293.66: 'D4',
311.13: 'D#4',
329.63: 'E4',
349.23: 'F4',
369.99: 'F#4',
392.0: 'G4',
415.3: 'G#4',
440.0: 'A4',
466.16: 'A#4',
493.88: 'B4',
523.25: 'C5',
};
let originalRecordedNotes = [];
let processedNotes = [];
let isMarkovChainProcessed = false;
let recordButton;
let isRecording = false;
let startTime;
let recordedNotes = [];
let recordingStartTime;
let isPlayingBack = false;
let playbackStartTime;
let osc;
let keyStates = {};
let notes = {
A: 261.63, // C
W: 277.18, // C#
S: 293.66, // D
E: 311.13, // D#
D: 329.63, // E
F: 349.23, // F
T: 369.99, // F#
G: 392.0, // G
Y: 415.3, // G#
H: 440.0, // A
U: 466.16, // A#
J: 493.88, // B
K: 523.25, // C
};
function setup() {
createCanvas(400, 350);
osc = new p5.Oscillator();
osc.setType("sine");
osc.amp(0);
osc.start();
for (let key in notes) {
keyStates[key] = false;
}
let buttonWidth = 80;
let buttonHeight = 40;
let recordButton = createButton("⬤");
recordButton.position(30, 60);
recordButton.size(buttonWidth, buttonHeight);
recordButton.mousePressed(startRecording);
recordButton.style("font-size", "20px")
recordButton.style("background-color", "red");
let playButton = createButton("▶");
playButton.position(110, 60);
playButton.size(buttonWidth, buttonHeight);
playButton.mousePressed(startPlayback);
playButton.style("font-size", "20px")
playButton.style("background-color", "green");
let markovButton = createButton("~");
markovButton.position(190, 60);
markovButton.size(buttonWidth, buttonHeight);
markovButton.mousePressed(applyMarkovChain);
markovButton.style("font-size", "30px")
markovButton.style("background-color", "lightblue");
let downloadButton = createButton("↓");
downloadButton.position(270, 60);
downloadButton.size(buttonWidth, buttonHeight);
downloadButton.mousePressed(downloadRecording);
downloadButton.style("font-size", "20px")
downloadButton.style("background-color", "white");
soundRecorder = new p5.SoundRecorder();
soundRecorder.setInput(osc);
soundFile = new p5.SoundFile();
}
function draw() {
background(220);
drawPianoKeys();
if (isMarkovChainProcessed) {
}
if (isPlayingBack) {
playbackRecordedNotes();
}
if (isMarkovChainProcessed) {
drawMarkovTimeline();
}
if (isMarkovChainProcessed) {
/*fill(0, 0, 255); // Blue
rect(width - 50, height - 50, 40, 40); //blue rect*/
}
drawNoteTimeline();
}
function getNotePosition(note) {
let x, y;
let whiteKeyWidth = 40;
let blackKeyWidth = 20;
let blackKeyOffset = 30;
let startY = 100;
let keyHeight = 160;
let whiteKeysOrder = ["A", "S", "D", "F", "G", "H", "J", "K"];
let blackKeysOrder = ["W", "E", "T", "Y", "U"];
let whiteKeyIndex = whiteKeysOrder.indexOf(note);
let blackKeyIndex = blackKeysOrder.indexOf(note);
if (whiteKeyIndex != -1) {
x = 20 + whiteKeyWidth * whiteKeyIndex + whiteKeyWidth / 2;
y = startY + keyHeight / 2;
} else if (blackKeyIndex != -1) {
x = 20 + blackKeyOffset + whiteKeyWidth * blackKeyIndex + blackKeyWidth / 2;
y = startY + keyHeight / 2;
} else {
x = -1;
y = -1;
}
return createVector(x, y);
}
function drawPianoKeys() {
stroke("black");
//white keys
let whiteKeys = ["A", "S", "D", "F", "G", "H", "J", "K"];
for (let i = 0; i < whiteKeys.length; i++) {
let key = whiteKeys[i];
fill(keyStates[key] ? "rgb(255,97,0)" : "white");
rect(30 + 40 * i, 100, 40, 160);
}
//black keys
let blackKeys = ["W", "E", "T", "Y", "U"];
let blackKeyOffsets = [30, 70, 150, 190, 230];
for (let i = 0; i < blackKeys.length; i++) {
let key = blackKeys[i];
fill(keyStates[key] ? "rgb(255,97,0)" : "black");
rect(blackKeyOffsets[i] + 30, 100, 20, 100);
}
}
function startRecording() {
if (!isRecording) {
recordedNotes = [];
isRecording = true;
recordingStartTime = millis();
setTimeout(() => {
isRecording = false;
}, 20000);
}
}
function keyPressed() {
let freq = notes[key.toUpperCase()];
if (freq) {
keyStates[key.toUpperCase()] = true;
osc.freq(freq);
osc.amp(0.5, 0.1);
if (isRecording) {
recordedNotes.push({
note: key.toUpperCase(),
startTime: millis(),
freq: freq,
});
}
}
}
function keyReleased() {
let freq = notes[key.toUpperCase()];
if (freq) {
keyStates[key.toUpperCase()] = false;
osc.amp(0, 0.1);
if (isRecording) {
let noteEvent = recordedNotes.find(
(n) => n.freq === freq && n.duration === undefined
);
if (noteEvent) {
noteEvent.duration = millis() - noteEvent.startTime;
}
}
}
}
function startPlayback() {
if (!isPlayingBack && recordedNotes.length > 0) {
console.log("Playback started");
playbackStartTime = millis();
isPlayingBack = true;
soundRecorder.record(soundFile);
setTimeout(stopPlayback, 10000);
}
}
function stopPlayback() {
if (isPlayingBack) {
console.log("Playback stopped");
isPlayingBack = false;
soundRecorder.stop();
osc.amp(0, 0.1);
}
}
function playbackRecordedNotes() {
let currentTime = millis() - playbackStartTime;
let currentNoteIndex = -1;
let notesToPlay = isMarkovChainProcessed ? processedNotes : recordedNotes;
for (let i = 0; i < notesToPlay.length; i++) {
let note = notesToPlay[i];
let noteStart = note.startTime;
let noteEnd = noteStart + note.duration;
if (currentTime >= noteStart && currentTime <= noteEnd) {
if (!note.isPlaying) {
osc.freq(note.freq);
osc.amp(0.5, 0.1);
note.isPlaying = true;
let key = getKeyFromFrequency(note.freq);
if (key) {
keyStates[key] = true;
}
currentNoteIndex = i;
}
} else if (note.isPlaying) {
osc.amp(0, 0.1);
note.isPlaying = false;
let key = getKeyFromFrequency(note.freq);
if (key) {
keyStates[key] = false;
}
}
}
if (currentNoteIndex != -1 && currentNoteIndex < notesToPlay.length - 1) {
let currentNote = notesToPlay[currentNoteIndex];
let nextNote = notesToPlay[currentNoteIndex + 1];
}
}
function getKeyFromFrequency(freq) {
for (let key in notes) {
if (notes[key] === freq) {
return key;
}
}
return null;
}
function generateMarkovChain() {
let markovModel = {};
for (let i = 0; i < recordedNotes.length - 1; i++) {
let currentNote = recordedNotes[i].note;
let nextNote = recordedNotes[i + 1].note;
if (!markovModel[currentNote]) {
markovModel[currentNote] = {};
}
if (!markovModel[currentNote][nextNote]) {
markovModel[currentNote][nextNote] = 0;
}
markovModel[currentNote][nextNote] += 1;
}
for (let currentNote in markovModel) {
let total = 0;
for (let nextNote in markovModel[currentNote]) {
total += markovModel[currentNote][nextNote];
}
for (let nextNote in markovModel[currentNote]) {
markovModel[currentNote][nextNote] /= total;
}
}
return markovModel;
}
function applyMarkovChain() {
let markovModel = generateMarkovChain();
let newSequence = [];
let currentNoteName = recordedNotes[0].note;
let cumulativeTime = 0;
for (let i = 0; i < recordedNotes.length - 1; i++) {
let currentNoteFreq = notes[currentNoteName];
newSequence.push({
note: currentNoteName,
freq: currentNoteFreq,
startTime: cumulativeTime,
duration: recordedNotes[i].duration,
});
cumulativeTime += recordedNotes[i].duration;
// Markov choose next note
if (markovModel[currentNoteName]) {
let nextNotes = markovModel[currentNoteName];
let totalProb = 0;
let rand = Math.random();
for (let nextNote in nextNotes) {
totalProb += nextNotes[nextNote];
if (rand <= totalProb) {
currentNoteName = nextNote;
break;
}
}
}
let lastNoteFreq = notes[currentNoteName];
newSequence.push({
note: currentNoteName,
freq: lastNoteFreq,
startTime: cumulativeTime,
duration: recordedNotes[recordedNotes.length - 1].duration,
});
processedNotes = newSequence;
isMarkovChainProcessed = true;
startPlaybackAfterMarkov();
markovProbabilities = markovModel;
}
let lastNoteFreq = notes[currentNoteName];
newSequence.push({
note: currentNoteName,
freq: lastNoteFreq,
startTime: cumulativeTime,
duration: recordedNotes[recordedNotes.length - 1].duration,
});
processedNotes = newSequence;
isMarkovChainProcessed = true;
}
function startPlaybackAfterMarkov() {
playbackStartTime = millis();
isPlayingBack = true;
for (let key in keyStates) {
keyStates[key] = false;
}
for (let note of recordedNotes) {
note.isPlaying = false;
}
}
function drawNoteTimeline() {
let startX = 30;
let y = 300;
let timeScale = 0.1;
for (let i = 0; i < recordedNotes.length; i++) {
let note = recordedNotes[i];
let lineLength = note.duration * timeScale;
stroke(0);
strokeWeight(10);
stroke("red");
line(startX, y, startX + lineLength, y);
strokeWeight(1);
textAlign(CENTER, BOTTOM);
fill(0);
let noteName = noteNames[note.freq];
text(noteName, startX + lineLength / 2, y - 5);
startX += lineLength + 10;
}
}
function drawMarkovTimeline() {
let startX = 30;
let y = 330;
let timeScale = 0.1;
for (let i = 0; i < processedNotes.length; i++) {
let note = processedNotes[i];
let lineLength = note.duration * timeScale;
stroke(0);
strokeWeight(10);
stroke("lightblue");
line(startX, y, startX + lineLength, y);
strokeWeight(1);
textAlign(CENTER, BOTTOM);
fill(0);
let noteName = noteNames[note.freq];
text(noteName, startX + lineLength / 2, y - 5);
startX += lineLength + 10;
}
}
function downloadRecording() {
if (soundFile && soundFile.duration() > 0) {
saveSound(soundFile, 'mySound.wav');
} else {
console.log("No recording available to download");
}
}