createCanvas(windowWidth, windowHeight*0.99);
recorder = new p5.SoundRecorder();
soundFile = new p5.SoundFile();
inputFile = createFileInput(loadScore);
let nowTimeLineNum = ceil((timeLineX - timeLinePos) / noteWidth);
for(let i = timeLineNum; i < nowTimeLineNum; ++i) {
if(T.timeline[i].isOn) setSound(T);
timeLineNum = nowTimeLineNum;
text("tick span:" + tickSpan, width/11, H/9);
line(timeLineX, H*0.15, timeLineX, H*1.97);
timeLineX += (noteWidth/tickSpan) * (noteWidth/frameRate());
loopButton.isOn ? (timeLineX=timeLinePos):(startButton.isOn=false);
function mousePressed() {
if(T.isHit(mouseX,mouseY)) key = T;
T.timeline.forEach((t,i)=>{
if(t.isHit(mouseX,mouseY)) {
target.isOn = !target.isOn;
if(T.isHit(mouseX,mouseY)) T.isOn = !T.isOn;
if(recordButton.isHit(mouseX,mouseY)) {
recorder.record(soundFile);
saveSound(soundFile, "PianoRoll.wav");
T.env.play(T.osc, 0, 0.1);
T.env.triggerRelease(T.osc);
freqValue = midiToFreq(note);
envelope = new p5.Envelope();
envelope.setADSR(0.001, 0.25, 0.01, 0.25);
envelope.setRange(0.2, 0);
function initController() {
let controllerSize = H/10;
let controllerX = width*0.3;
let controllerY = height*0.02;
controllers.push(timelineButton = Object.assign(swichRect(WHITE,0,controllerY+controllerSize,width-timeLinePos,H/24),{
timelineButton.isOn = false;
timeLineX = max(mouseX,timeLinePos);
controllers.push(tickDecButton = Object.assign(swichRect(WHITE,width*0.05,controllerY,controllerSize,controllerSize),{
triangle(this.x + this.w*0.8, this.y + this.h*0.2, this.x + this.w*0.8, this.y + this.h*0.8, this.x + this.w*0.2, this.y + this.h*0.5);
tickDecButton.setResponse();
if(tickSpan > 1) --tickSpan;
tickDecButton.isDisable = (tickSpan <= 1);
controllers.push(tickAddButton = Object.assign(swichRect(WHITE,width*0.22,controllerY,controllerSize,controllerSize),{
triangle(this.x + this.w*0.2, this.y + this.h*0.2, this.x + this.w*0.2, this.y + this.h*0.8, this.x + this.w*0.8, this.y + this.h*0.5);
tickAddButton.setResponse();
tickDecButton.isDisable = (tickSpan <= 1);
controllers.push(recordButton = Object.assign(swichRect(WHITE,controllerX,controllerY,controllerSize,controllerSize),{
circle(this.x + this.w*0.5, this.y + this.h*0.5, controllerSize*0.7);
controllerX += controllerSize * 1.1;
controllers.push(startButton = Object.assign(swichRect(WHITE,controllerX,controllerY,controllerSize,controllerSize),{
triangle(this.x + this.w*0.2, this.y + this.h*0.2, this.x + this.w*0.2, this.y + this.h*0.8, this.x + this.w*0.8, this.y + this.h*0.5);
if(timeLineX>width) timeLineX = timeLinePos;
controllerX += controllerSize * 1.1;
controllers.push(pauseButton = Object.assign(swichRect(WHITE,controllerX,controllerY,controllerSize,controllerSize),{
rect(this.x + this.w*0.2, this.y + this.h*0.2, this.w*0.2, this.h*0.6);
rect(this.x + this.w*0.6, this.y + this.h*0.2, this.w*0.2, this.h*0.6);
startButton.isOn = false;
pauseButton.setResponse();
controllerX += controllerSize * 1.1;
controllers.push(stopButton = Object.assign(swichRect(WHITE,controllerX,controllerY,controllerSize,controllerSize),{
rect(this.x + this.w*0.225, this.y + this.h*0.225, this.w*0.55, this.h*0.55);
startButton.isOn = false;
stopButton.setResponse();
controllerX += controllerSize * 1.1;
controllers.push(loopButton = Object.assign(swichRect(WHITE,controllerX,controllerY,controllerSize,controllerSize),{
strokeWeight(this.w*0.15);
triangle(this.x + this.w*0.81, this.y + this.h*0.55, this.x + this.w*0.65, this.y + this.h*0.6, this.x + this.w*0.82, this.y + this.h*0.72);
arc(this.x + this.w/2, this.y + this.h/2, this.w*0.6, this.h*0.6, TAU*0.1, TAU*0.9);
controllerX += controllerSize * 1.1;
controllers.push(resetButton = Object.assign(swichRect(WHITE,controllerX,controllerY,controllerSize*2,controllerSize),{
textAlign(CENTER,CENTER);
text("RESET",this.x + this.w/2,this.y + this.h/2);
resetButton.setResponse();
controllerX += controllerSize * 2.8;
controllers.push(saveButton = Object.assign(swichRect(WHITE,controllerX,controllerY,controllerSize*2,controllerSize),{
textAlign(CENTER,CENTER);
text("SAVE",this.x + this.w/2,this.y + this.h/2);
saveButton.setResponse();
controllerX += controllerSize * 2.2;
controllers.push(loadButton = Object.assign(swichRect(WHITE,controllerX,controllerY,controllerSize*2,controllerSize),{
textAlign(CENTER,CENTER);
text("LOAD",this.x + this.w/2,this.y + this.h/2);
loadButton.setResponse();
for(let i = 0; i < 3; ++i) {
let offset = height - keyH*1.8 - keyH * 7 * i;
keys.push(keyRect(note , WHITE, 0, offset - keyH * num++, keyW, keyH));
keys.push(keyRect(note+ 2, WHITE, 0, offset - keyH * num++, keyW, keyH));
keys.push(keyRect(note+ 4, WHITE, 0, offset - keyH * num++, keyW, keyH));
keys.push(keyRect(note+ 5, WHITE, 0, offset - keyH * num++, keyW, keyH));
keys.push(keyRect(note+ 7, WHITE, 0, offset - keyH * num++, keyW, keyH));
keys.push(keyRect(note+ 9, WHITE, 0, offset - keyH * num++, keyW, keyH));
keys.push(keyRect(note+11, WHITE, 0, offset - keyH * num++, keyW, keyH));
keys.push(keyRect(note+ 1, BLACK, 0, offset - keyH * num++ + keyH*0.1, keyW, keyH*0.6));
keys.push(keyRect(note+ 3, BLACK, 0, offset - keyH * num++ - keyH*0.1, keyW, keyH*0.6));
keys.push(keyRect(note+ 6, BLACK, 0, offset - keyH * num++ + keyH*0.1, keyW, keyH*0.6));
keys.push(keyRect(note+ 8, BLACK, 0, offset - keyH * num++, keyW, keyH*0.6));
keys.push(keyRect(note+10, BLACK, 0, offset - keyH * num++ - keyH*0.1, keyW, keyH*0.6));
initTimeline=(x,y,w,h)=>{
for(let i = x; i < width; i += w) {
timeline.push(swichRect(timeline.length%4?GRAY:GRAY*0.7,i,y,w,h));
keyRect=(note,c,x,y,w,h)=>Object.assign(swichRect(c,x,y,w,h),{
timeline:initTimeline(timeLinePos,y,noteWidth,h),
swichRect=(c,x,y,w,h)=>({
isHit:function(targetX,targetY) {
return !this.isDisable && this.collision(targetX,targetY);
rect(this.x, this.y, this.w, this.h);
setPushColor:function() {
let setA = this.isDisable ? this.a/3 : this.a;
if(this.responseCount > 0) --this.responseCount;
fill((this.isOn || this.responseCount > 0) ? this.c : this.c + 90, setA);
collision:function(targetX,targetY) {
return targetX > this.x && targetX < this.x+this.w && targetY > this.y && targetY < this.y+this.h;
timelineData += (t.isOn?"1":"0");
saveJSON({tickSpan, data}, "PianoRoll.json");
if(file.type === "application" && file.subtype === "json") {
let base64Data = file.data.split(",")[1];
let decodedData = atob(base64Data);
jsonData = JSON.parse(decodedData);
console.error("Failed to parse json file : ", error);
console.error("Not a json file");
if(!("tickSpan" in jsonData) || !("data" in jsonData)) {
console.error("Validation error");
let data = jsonData.data[j];
T.timeline.forEach((t,i)=>{
t.isOn = !!data.charAt(i);
tickSpan = jsonData.tickSpan;