World (of MPP)'s Simplest note recorder!

220 views
Skip to first unread message

Lamp

unread,
Aug 2, 2017, 5:14:32 PM8/2/17
to mpp-...@googlegroups.com
I made a very simple note recorder:

Record
var recording = [];
MPP
.client.on('n', msg => {
    recording
.push(msg);
});


Playback
recording.forEach(msg => {
    setTimeout
(()=>{
        msg
.n.forEach(n => {MPP.press(n.n, n.v)});
   
}, msg.t - recording[0].t)
});


The recording part simply collects the note objects from the 'n' event into an array, and the playback part uses the timestamps of each object to play each pack of notes in sequence.
It works fairly well for how simple it is, but the result is kinda choppy and it doesn't do key releases.

The objects given by the 'n' event are actually bundles of notes, which have different delays, and the client has to take them apart and synchorize them before sending them to the piano.
If you look at the "Playing notes" part in the script.js (ctrl-f "n"), there is some more work going on. So I based on that.



Here is the second simplest note recorder, which records and plays back exactly the way you heard it!

Record
var recording = [];
MPP.client.on("n", function(msg) {
var t = msg.t - MPP.client.serverTimeOffset + 1000 - Date.now();
msg.n.forEach((note, i) => {
var ms = t + (note.d || 0);
if (ms < 0) ms = 0; else if (ms > 10000) return;
if (note.s) {
recording.push({tp:"r", ky:note.n, ts:Date.now()+ms}); // key releases
} else {
var vel = typeof note.v !== "undefined" ? parseFloat(note.v) : 0.5;
if (vel < 0) vel = 0; else if (vel > 1) vel = 1;
recording.push({tp:"p", ky:note.n, ts:Date.now()+ms, vl:vel}); // key presses
}
});
});

Playback
recording.forEach(note => {
setTimeout(()=>{
if (note.tp === "p") MPP.press(note.ky, note.vl);
if (note.tp === "r") MPP.release(note.ky);
}, note.ts - recording[0].ts);
});


To use it, paste the record part into your console and it'll record the notes into the "recording" array.
Then paste the playback code and it'll play back! Press backspace to release sustain pedal so key releases work.
To clear the recording, type "recording = []"
You can use JSON.stringify() to save to localStorage or to file! To save to localStorage: localStorage["name of your rec"] = JSON.stringify(recording);

Video

SopaPlay

unread,
Aug 3, 2017, 10:14:15 PM8/3/17
to Multiplayer Piano Forum
That wont work, by copy and paste, if the gives error for SyntaxError
i using old chrome and windows xp. did you fix it?

Lamp

unread,
Aug 9, 2017, 3:08:56 AM8/9/17
to mpp-...@googlegroups.com
I made a newer version that saves much smaller data. Instead of saving to a JSON object (and then stringifying that) it appends the notes to a string, where each note is separated by a semicolon and each key by a comma. All press notes have a velocity key but release notes don't, so that's what determines its type. Also the timestamps count from the recording start time.
So here's an example of a recording:
;2337,ds4,0.859;2337,b2,0.859;2456,ds4;
Three notes here. Last one is a release note. Each note consists of keys, first is timestamp, second is key, and third is velocity if it's a press note.
So to decode this, we just split it at the semicolons, then for each item (ignoring first and last cause they're blank), split at the commas.

bla bla here's the code

//record
var recording = ";";
var startTime = Date.now();

MPP.client.on("n", msg => {

       
var t = msg.t - MPP.client.serverTimeOffset + 1000 - Date.now();
       
msg.n.forEach((note, i) => {
               
var ms = t + (note.d || 0);
               
if (ms < 0) ms = 0; else if (ms > 10000) return;
               
if (note.s) {

                       
recording += `${Date.now()+ms-startTime},${note.n};`; // key releases
               
} else {
                       
var vel = note.v ? parseFloat(note.v) : 0.5;

                       
if (vel < 0) vel = 0; else if (vel > 1) vel = 1;

                       
recording += `${Date.now()+ms-startTime},${note.n},${vel};`; // key presses
               
}
       
});
});

// playback
var timers = [];
recording.split(';').slice(1,-1).forEach(note => {
       
var keys = note.split(',');
       
timers.push(setTimeout(()=>{
               
if (keys[2]) MPP.press(keys[1], Number(keys[2]));
               
else MPP.release(keys[1]);
   
}, keys[0]));
});

Above is for the interest of people who understand Javascript, but if you're not that high, I made something a little more user friendly:

// Basic note recorder for use in web console, made by Lamp
// To use, paste into console, then control with the following functions (type into console)
// record(), stopRecord(), playRecording(), stopPlayingRecording(), saveRecording("name"), loadRecording("name"), and resetRecording()

var recording = ";";
var isRecording = false;
var startTime;
var timers = [];

MPP.client.on("n", msg => {

       
if (!isRecording) return;

       
var t = msg.t - MPP.client.serverTimeOffset + 1000 - Date.now();
       
msg.n.forEach((note, i) => {
               
var ms = t + (note.d || 0);
               
if (ms < 0) ms = 0; else if (ms > 10000) return;
               
if (note.s) {

                       
recording += `${Date.now()+ms-startTime},${note.n};`; // key releases
               
} else {
                       
var vel = note.v ? parseFloat(note.v) : 0.5;

                       
if (vel < 0) vel = 0; else if (vel > 1) vel = 1;

                       
recording += `${Date.now()+ms-startTime},${note.n},${vel};`; // key presses
               
}
       
});
});
function record() {
       
startTime = Date.now();
       
isRecording = true;
}
function stopRecord() {
       
isRecording = false;
}
function playRecording() {
       
recording.split(';').slice(1,-1).forEach(note => {
               
var keys = note.split(',');
               
timers.push(setTimeout(()=>{
                       
if (keys[2]) MPP.press(keys[1], Number(keys[2]));
                       
else MPP.release(keys[1]);
               
}, keys[0]));
       
});
}
function stopPlayingRecording() {
       
timers.forEach(timer => {clearTimeout(timer)});
       
timers = [];
}
function saveRecording(name) {
       
localStorage[name] = recording;
}
function loadRecording(name) {
       
recording = localStorage[name];
}
function resetRecording() {
       
recording = ";";
}

You can just paste all that into console, then call the functions to control it.

A fully-featured user-friendly userscript is todo

LGPvS

unread,
May 22, 2018, 7:49:58 AM5/22/18
to mpp-...@googlegroups.com
how to fix bug if its dont records yourself notes???


Lamp

unread,
Jun 27, 2018, 5:30:29 PM6/27/18
to Multiplayer Piano Forum
On Tuesday, May 22, 2018 at 4:49:58 AM UTC-7, LGPvS wrote:
how to fix bug if its dont records yourself notes???

 hmm, you know what, instead of listening and parsing "n" events, you could just modify the gPiano.play() and gPiano.stop() functions to collect notes, since those are the functions that make the piano simply play. They're the final destionations of both the built-in "n" event listener, and the press() and release() functions that are used for your own notes. That would be even simpler and work perfectly!!

Lamp

unread,
Jun 28, 2018, 11:47:33 PM6/28/18
to mpp-...@googlegroups.com

// record
var recording = new Array();
MPP
.piano._play = MPP.piano.play;
MPP
.piano._stop = MPP.piano.stop;
MPP
.piano.play = function (note, vol, participant, delay_ms) {
    recording
.push([note, vol, Date.now() + delay_ms]);
    MPP
.piano._play(note, vol, participant, delay_ms);
};
MPP
.piano.stop = function (note, participant, delay_ms) {
    recording
.push([note, null, Date.now() + delay_ms]);
    MPP
.piano._stop(note, participant, delay_ms);
};


// playback
for (let note of recording) {
    setTimeout
(()=>{
        
if (note[1]) MPP.press(note[0], note[1]);
        
else MPP.release(note[0]);
    
}, note[2] - recording[0][2]);
}

Lamp

unread,
Jun 29, 2018, 12:41:15 AM6/29/18
to mpp-...@googlegroups.com
here is another playback method for the above example, using a recursing function:


(function playback(i = 0){
   
var note = recording[i];

   
if (note[1]) MPP.press(note[0], note[1]);
   
else MPP.release(note[0]);

    setTimeout
(playback, note[2] - recording[i-1][2], i);
})()




This keeps on playing as the recorder records so you have a loop

I can't seem to get it working right though

LGPvS

unread,
Aug 17, 2018, 12:14:15 PM8/17/18
to Multiplayer Piano Forum


how to make its record only your notes?

Lamp

unread,
Aug 17, 2018, 8:02:00 PM8/17/18
to Multiplayer Piano Forum
you'd have to modify the press() and release() functions in the script.js; they represent your own key presses and releases. Unfortunately you can't just assign to MPP.press and MPP.release because of the way function assignments work (assigning a function makes a copy of that function, while assigning an object makes a reference to the same object, hence why we can assign to MPP.piano.play and have the internal gPiano.play use the same function; MPP.piano and internal gPiano are the same object; MPP object references the piano object. But MPP.press is just a copy of the internal press function so assigning a new function to MPP.press won't affect the internal `press`)

LGPvS

unread,
Aug 31, 2018, 8:01:17 AM8/31/18
to Multiplayer Piano Forum
How to record notes by _id?

Lamp

unread,
Aug 31, 2018, 11:54:36 AM8/31/18
to Multiplayer Piano Forum
The piano .play and .stop functions have a `participant` argument so you can just include `participant._id` into your recording.

LGPvS

unread,
Sep 21, 2018, 11:43:19 AM9/21/18
to Multiplayer Piano Forum
Give script that records notes of specified player
Reply all
Reply to author
Forward
0 new messages