xxxxxxxxxx
419
let noteNames = {
261.63: 'C',
277.18: 'C#',
293.66: 'D',
311.13: 'D#',
329.63: 'E',
349.23: 'F',
369.99: 'F#',
392.0: 'G',
415.3: 'G#',
440.0: 'A',
466.16: 'A#',
493.88: 'B',
523.25: 'C',
};
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(500, 500);
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(50, 20);
recordButton.size(buttonWidth, buttonHeight);
recordButton.mousePressed(startRecording);
recordButton.style("background-color", "red");
let playButton = createButton("▶");
playButton.position(160, 20);
playButton.size(buttonWidth, buttonHeight);
playButton.mousePressed(startPlayback);
playButton.style("background-color", "green");
let markovButton = createButton("~");
markovButton.position(260, 20);
markovButton.size(buttonWidth, buttonHeight);
markovButton.mousePressed(applyMarkovChain);
markovButton.style("background-color", "lightblue");
}
function draw() {
background(220);
drawPianoKeys();
if (isPlayingBack) {
playbackRecordedNotes();
}
//Markov Chain transitions
if (isMarkovChainProcessed) {
drawMarkovTimeline();
}
// markov done rect
if (isMarkovChainProcessed) {
/*fill(0, 0, 255); // Blue
rect(width - 50, height - 50, 40, 40); //blue rect*/
}
drawNoteTimeline();
}
// Function to draw lines representing Markov Chain transitions
function drawButtons() {
/*fill("red"); // Color for the buttons
rect(20, 20, 80, 40); // Record button
fill("green"); // Color for the buttons
rect(120, 20, 80, 40); // Play button
fill("blue"); // Color for the buttons
rect(220, 20, 80, 40); // Markov Chain button
fill(255);
textAlign(CENTER, CENTER);
textSize(18);
text("Record", 60, 40);
text("Play", 160, 40);
text("Markov", 260, 40);*/
}
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(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,
// 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;
let notesToPlay = isMarkovChainProcessed ? processedNotes : recordedNotes; // Use processedNotes when Markov chain is applied
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; //current note
}
} 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];
}
}
//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 the first note
let cumulativeTime = 0;
for (let i = 0; i < recordedNotes.length - 1; i++) {
// Recorded note iterations
let currentNoteFreq = notes[currentNoteName]; // Current note's frequency
newSequence.push({
note: currentNoteName,
freq: currentNoteFreq, // Store frequency
startTime: cumulativeTime,
duration: recordedNotes[i].duration, // Original sequence duration
});
cumulativeTime += recordedNotes[i].duration;
// Markov choose the 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; // Store the processed notes separately
isMarkovChainProcessed = true; // Markov finished flag
startPlaybackAfterMarkov(); // Start playback after applying Markov chain
}
let lastNoteFreq = notes[currentNoteName];
newSequence.push({
note: currentNoteName,
freq: lastNoteFreq,
startTime: cumulativeTime,
duration: recordedNotes[recordedNotes.length - 1].duration,
});
processedNotes = newSequence; // Store the processed notes separately
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;
}
}
function drawNoteTimeline() {
let startX = 10; // Starting X position for the timeline
let y = 360; // Y position of the timeline
let timeScale = 0.1; // Scale to adjust the length of the lines
for (let i = 0; i < recordedNotes.length; i++) { // Use recordedNotes for visualization
let note = recordedNotes[i];
let lineLength = note.duration * timeScale; // Length of the line based on the duration of the note
// Draw the line for the note
stroke(0);
line(startX, y, startX + lineLength, y);
// Draw the note name above the line
textAlign(CENTER, BOTTOM);
fill(0);
let noteName = noteNames[note.freq];
text(noteName, startX + lineLength / 2, y - 5);
//text(note.note, startX + lineLength / 2, y - 5); // Display note name above the line
// Update the starting X position for the next note
startX += lineLength + 10; // Add some space between lines
}
}
function drawMarkovTimeline() {
let startX = 10; // Starting X position for the timeline
let y = 390; // Y position of the timeline
let timeScale = 0.1; // Scale to adjust the length of the lines
for (let i = 0; i < processedNotes.length; i++) { // Use recordedNotes for visualization
let note = processedNotes[i];
let lineLength = note.duration * timeScale; // Length of the line based on the duration of the note
// Draw the line for the note
stroke(0);
line(startX, y, startX + lineLength, y);
// Draw the note name above the line
textAlign(CENTER, BOTTOM);
fill(0);
let noteName = noteNames[note.freq];
text(noteName, startX + lineLength / 2, y - 5);
//text(note.note, startX + lineLength / 2, y - 5); // Display note name above the line
// Update the starting X position for the next note
startX += lineLength + 10; // Add some space between lines
}
}