Closed wustep closed 2 years ago
Hi @wustep,
I think it's just a matter of using NoteEvent
wrapper instead of NoteOnEvent
. NoteOnEvent
isn't meant to be used like that. This should work:
const noteEvent = {
pitch: KEY_TO_NOTE[message.data[1]],
velocity: message.data[2],
startTick:
"158"
};
console.log(noteEvent);
events.push(new MidiWriter.NoteEvent(noteEvent));
-Garrett
hi Garrett!
Thanks for the quick reply -- was a bit confused cause docs mention NoteOn and NoteOff. I tried editing main.js and using NoteOn/Off but am having timing issues with that (but maybe I just need to figure out how ticks work).
So -- my use-case is recording MIDI input and creating MIDI output, so I have a bunch of events like:
messages =
[{ data: [ 144, 64, 80 ], timestamp: 2020737.209999992 }
{ data: [ 144, 60, 80 ], timestamp: 2020777.28499996 }
...
{ data: [ 128, 67, 0 ], timestamp: 2021860.4849999538 }
{ data: [ 144, 67, 80 ], timestamp: 2021981.9349999889 }
{ data: [ 128, 71, 0 ], timestamp: 2021983.889999974 }
{ data: [ 128, 67, 0 ], timestamp: 2022004.9349999754 }
{ data: [ 128, 69, 0 ], timestamp: 2022005.2499999874 }];
where timestamp
is the time in milliseconds.
NoteEvent
doesn't work for me, as I'm not specifying duration (I don't know duration without pre-processing), so I get:
const noteEvent = {
pitch: NOTE_TO_KEY[message.data[1]],
velocity: message.data[2],
startTick: Math.floor(message.timestamp - recordingTimestamp)
};
console.log(noteEvent);
track.addEvent(new MidiWriter.NoteEvent(noteEvent));
TypeError: Cannot read property 'toString' of undefined
at Function.getTickDuration (.../node_modules/midi-writer-js/build/index.js:287:27)
at new NoteEvent (.../node_modules/midi-writer-js/build/index.js:536:33)
This is my full (dirty) testing code to help you understand my use-case. I don't think I'm doing tempo correctly (goal is 100 bpm later)!
export const writeMidi = (messages, recordingTimestamp) => {
const track = new MidiWriter.Track();
const events = [];
for (let message of messages) {
if (message.data[0] === MIDI_EVENTS.NOTE_ON) {
events.push(new MidiWriter.NoteOnEvent({
pitch: NOTE_TO_KEY[message.data[1]],
velocity: message.data[2],
startTick: Math.floor(message.timestamp - recordingTimestamp)
};));
} else if (message.data[0] === MIDI_EVENTS.NOTE_OFF) {
events.push(
new MidiWriter.NoteOffEvent({
pitch: NOTE_TO_KEY[message.data[1]],
startTick: Math.floor(message.timestamp - recordingTimestamp),
duration: 0 /* need this here or noteOff will error */
})
);
}
}
track.setTempo(1000);
track.addEvent(events);
const write = new MidiWriter.Writer(track);
console.log(write.dataUri());
const randomFileName = Math.random()
.toString(36)
.substring(7);
write.saveMIDI("./records/" + randomFileName);
console.log(`Recorded to ${randomFileName}.midi`);
};
This compiles, but the output is not as expected (all notes are played at once for a short period of time at some time) -- let me know what you think!
thanks! -stephen
Hi Stephen,
Ah I gotcha. Interesting, yea I suppose if NoteOnEvent
and NoteOffEvent
were exported from main.js you could use them this way. I haven't tested interfacing with them publicly like that, but it certainly seems like what you need. I'm sure others would find it useful too.
I can look into that a little later, or if like feel free to submit a PR.
-Garrett
Got it! Thanks -- might try to tackle this in a few days.
In the meantime -- do you know what I should do for tempo? If I setTempo(60)
, what should startTicks
be if I have timestamp
in milliseconds?
e.g. C4 is played @ 1 second in, then let go at 2 seconds. Is startTick=1
for the NoteOn and 2
for the NoteOff? Or is it 128
for the first and 256
for the second?
@grimmdude , I was about to use your library in order to serialize WebMIDI to files (let you download what you just played) - but I can't do that because the library kinda forces me to use the duration mechanism (I forked the code and started changing it, but NoteOff isn't written for such a scenario). Any tips on how to write MIDI files from plain NoteOn/Off events without changing much of your code?
Hi @wustep and @thebne, I've exported the NoteOnEvent
and NoteOffEvent
classes to main so they are accessible directly now. I haven't done much testing with it, but hopefully this should give you the flexibility you need. I'll write some tests for them when I have more time, but let me know if this helps out. Released version 1.7.3 with this change.
-Garrett
@grimmdude thanks, I ended up using midi-file for now. I think right now it's a bit tricky to use the NoteOn/Off
functionality because I'd have to apply math on the ticks to make it work.
Hey @thebne,
@grimmdude , I was about to use your library in order to serialize WebMIDI to files (let you download what you just played) - but I can't do that because the library kinda forces me to use the duration mechanism (I forked the code and started changing it, but NoteOff isn't written for such a scenario).
If you have a file, array buffer, or data uri you can use https://github.com/grimmdude/MidiPlayerJS as a parser/serializer as well if you like. To get MIDI events serialized to JSON you can do something like this:
const MidiPlayer = require('midi-player-js');
const Player = new MidiPlayer.Player;
Player.loadDataUri(/*dataUri*/);
Player.tracks.map(function(track) {
// MIDI event json can be found here for each track.
console.log(track.events);
});
Example json for an event:
{
"byteIndex": 104,
"channel": 1,
"delta": 1,
"name": "Note on",
"noteName": "E5",
"noteNumber": 76,
"running": true,
"tick": 120,
"track": 1
}
Hi! thankful for this project -- ran into issues trying to use NoteOn / NoteOff!
Error:
TypeError: _midiWriterJs.default.NoteOnEvent is not a constructor
orNoteOffEvent
.Code:
I think this is a quick fix -- exporting the events in main.js?