xxxxxxxxxx
372
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, 400);
osc = new p5.Oscillator();
osc.setType("sine");
osc.amp(0);
osc.start();
for (let key in notes) {
keyStates[key] = false;
}
let recordButton = createButton("Record");
recordButton.mousePressed(startRecording);
let playButton = createButton("Play");
playButton.mousePressed(startPlayback);
let markovButton = createButton("Markov Chain");
markovButton.mousePressed(applyMarkovChain);
}
function draw() {
background(220);
drawPianoKeys();
if (isPlayingBack) {
playbackRecordedNotes();
}
//Markov Chain transitions
if (isMarkovChainProcessed) {
drawMarkovChainLines();
}
// markov done rect
if (isMarkovChainProcessed) {
fill(0, 0, 255); // Blue
rect(width - 50, height - 50, 40, 40); //blue rect
}
}
// Function to draw lines representing Markov Chain transitions
function drawMarkovChainLines() {
/*
let markovModel = generateMarkovChain();
stroke(0, 102, 153); // Set the color for the lines
for (let note in markovModel) {
let startPos = getNotePosition(note); // Get the start position of the line
for (let nextNote in markovModel[note]) {
let endPos = getNotePosition(nextNote); // Get the end position of the line
let probability = markovModel[note][nextNote]; // Probability of transition
strokeWeight(probability * 5); // Line thickness based on probability
// Define control points for the Bezier curve
let controlPoint1 = createVector(startPos.x, startPos.y - 100);
let controlPoint2 = createVector(endPos.x, endPos.y - 100);
noFill();
bezier(startPos.x, startPos.y, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPos.x, endPos.y);
}
}
*/
}
function getNotePosition(note) {
let x, y;
let whiteKeyWidth = 40;
let blackKeyWidth = 20;
let blackKeyOffset = 30; // Offset for the black key from the start of the white key
let startY = 100; // Y position of keys (as per your drawPianoKeys function)
let keyHeight = 160; // Height of the keys
// Define the order of the keys on the piano
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) {
// It's a white key
x = 20 + whiteKeyWidth * whiteKeyIndex + whiteKeyWidth / 2; // Center of the white key
y = startY + keyHeight / 2; // Middle of the key height
} else if (blackKeyIndex != -1) {
// It's a black key
x = 20 + blackKeyOffset + whiteKeyWidth * blackKeyIndex + blackKeyWidth / 2; // Center of the black key
y = startY + keyHeight / 2; // Middle of the key height
} else {
// If the note is not found, return a default position
x = -1;
y = -1;
}
return createVector(x, y);
}
function drawPianoKeys() {
//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(20 + 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] + 20, 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,
// Duration will be calculated on key release
});
}
}
}
function keyReleased() {
let freq = notes[key.toUpperCase()];
if (freq) {
keyStates[key.toUpperCase()] = false;
osc.amp(0, 0.1);
if (isRecording) {
// Find the last recorded note with the same frequency that hasn't been finished yet
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) {
playbackStartTime = millis();
isPlayingBack = true;
}
}
function playbackRecordedNotes() {
let currentTime = millis() - playbackStartTime;
let currentNoteIndex = -1;
for (let i = 0; i < recordedNotes.length; i++) {
let note = recordedNotes[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; //current note
}
} else if (note.isPlaying) {
osc.amp(0, 0.1);
note.isPlaying = false;
let key = getKeyFromFrequency(note.freq);
if (key) {
keyStates[key] = false;
}
}
}
//Bezier curve for next note
if (currentNoteIndex != -1 && currentNoteIndex < recordedNotes.length - 1) {
let currentNote = recordedNotes[currentNoteIndex];
let nextNote = recordedNotes[currentNoteIndex + 1];
drawBezierCurveForNotes(currentNote.note, nextNote.note);
}
if (currentNoteIndex != -1 && currentNoteIndex < recordedNotes.length - 1) {
let currentNote = recordedNotes[currentNoteIndex];
let nextNote = recordedNotes[currentNoteIndex + 1];
drawBezierCurveForNotes(currentNote.note, nextNote.note);
}
}
function drawBezierCurveForNotes(currentNoteName, nextNoteName) {
let startPos = getNotePosition(currentNoteName);
let endPos = getNotePosition(nextNoteName);
stroke(0, 102, 153);
strokeWeight(2);
let controlPointHeight = 150;
let controlPoint1 = createVector(
(startPos.x + endPos.x) / 2,
startPos.y - controlPointHeight
);
let controlPoint2 = createVector(
(startPos.x + endPos.x) / 2,
endPos.y - controlPointHeight
);
noFill();
bezier(
startPos.x,
startPos.y,
controlPoint1.x,
controlPoint1.y,
controlPoint2.x,
controlPoint2.y,
endPos.x,
endPos.y
);
}
//key from freq
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;
}
//count to probabilities
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; //start from first note
let cumulativeTime = 0;
for (let i = 0; i < recordedNotes.length - 1; i++) {
//recorded note iterations
let currentNoteFreq = notes[currentNoteName]; //current note get freq
newSequence.push({
note: currentNoteName,
freq: currentNoteFreq, //store freq
startTime: cumulativeTime,
duration: recordedNotes[i].duration, // original seq 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,
});
recordedNotes = newSequence; // new seq to recordedNotes
startPlaybackAfterMarkov(); // playback start
isMarkovChainProcessed = true; //markov finished flag
}
function startPlaybackAfterMarkov() {
playbackStartTime = millis();
isPlayingBack = true;
for (let key in keyStates) {
keyStates[key] = false;
}
for (let note of recordedNotes) {
note.isPlaying = false;
}
}