djipco / webmidi

Tame the Web MIDI API. Send and receive MIDI messages with ease. Control instruments with user-friendly functions (playNote, sendPitchBend, etc.). React to MIDI input with simple event listeners (noteon, pitchbend, controlchange, etc.).
Apache License 2.0
1.54k stars 115 forks source link

Octave number inconsistent #19

Closed cfry closed 7 years ago

cfry commented 7 years ago

//It appears that when WebMidi sends a noteon message to itself, //the octave in the listened for note is 2 octaves below //the octave that was sent. //Code to reproduce: //eval the commented out last line to send C3.

var wmin, wmout //WebMidiInput and WebMidiOutput ports
WebMidi = require("webmidi")
WebMidi.enable(function(err) {
    if (err) {out("WebMidi couldn't be enabled: " + err)}
    else {
     out(WebMidi.inputs)
     out(WebMidi.outputs)
     wmin  = WebMidi.inputs[0] 
     wmout = WebMidi.outputs[0] //use same output as the output so you can "hear" what's "played"

     wmin.addListener('noteon',  "all",
                    function(event){
                            debugger
                            console.log(event.note.octave)})
  }})

// webMidi: C3 == 60 == middle C. GarageBand also plays 60 as middle C // wmout.playNote("C3", 1, {duration: 2000})

/*setting up a Mac or PC to be able to do this without a hardware midi controller is difficult. Here's how: To get a MIDI app on your computer to talk to another MIDI app on your computer, you must set up a "Virtual Port".

On Mac 10.12 (Sierra) Launch Applications/Utilities/Audio Midi Setup.app Choose menubar/Window/Show Midi Studio In the dialog box that comes up, double click "IAC Driver". In the new dialog box that comes up, click on the port "Bus 1" to select it. Above that, check "Device is online" Click the "Apply" button.

For creating virtual ports on windows: http://www.tobias-erichsen.de/software/loopmidi.html looks promising.

Eval the below to enable webmidi and create an input and output.

Eval the last line (playNote call) to send a note from WebMidi, through the "Bus 1" port, and back to WebMidi. You should see in the output pane: "Got MIDI event: Input, IAC Driver Bus 1, channel:1, type: noteon, C1, vel: 0.5039370078740157"

To hear this on a "software synthesizer" you have to get it to listen to midi output on the appropriate device, on the appropriate channel.

One such software synthesizer is in GarageBand, which is now free for Mac and Windows.

On Mac, In your Applications folder you should see GarageBand.app (comes with the OS) Double click on it. This will cause its full contents to download and takes 15 minutes or so (its very large). Once this is complete, launch it if it isn't already up.

On Windows, see https://garagebandforwindowspc.org/ for a more complex installation, but looks promising.

With GB launched, double click on "Keyboard Collection" Back in DDE, Eval the call to playNote and you should hear a sound. Select a different instrument in GarageBand for a different sound. Debugging Garage Band: If no sound, EVAL playnote in DDE again. Sometimes the first one just doesn't work. If that doesn't work, check that sound is up on your computer. (On Mac, hit the F12 key a few times.) If that didn't help, verify that Garage Band can make a sound by choosing its menu bar/Show Keyboard Then click on the piano keys and you should hear a sound. If that didn't help, check GB's "Master Slider" (unlabeled horizontal slider in the upper right of GB main window.) If that didn't help, check the midi connections in Garage Band: menu bar/GarageBand/Preferences. Select "Audio/Midi" In the bottom of the dialog you should see "Midi Status: X MIDI inputs detected." (if X == 0, try clicking the "Reset Midi Drivers" button.)

Even though GB has a soft keyboard, it can't send out Midi. You can download http://vmpk.sourceforge.net/ for a virtual midi controller, for Mac/Windows/Linux. It has no synthesizers in it. In the VMPK menu bar, choose "MIDI Connections". For "MIDI OUT Driver", choose "CoreMIDI". For "Output MIDI Connection" Choose "IAC Driver Bus 1" (the default on the Mac, it will be something else on Windows.) Click the OK button. Choose VMPK menu bar/Tools/Note Input/Comuter Keyboard, and also in the same sub menu, "Mouse". Now click on the keyboard or hit a key (like "q") and you should see "Got Midi event..." in DDE. You should also hear the note played in Garage Band.

VMPK has a nice feature for labeling the keys on its piano keyboard: Choose menu bar/View/Note names Unfortunately this labels Middle C as C4, whereas WebMidi uses Middle C as C3. Octave nomenclature is inconsistent in conventional music as well as MIDI. */

cfry commented 7 years ago

I just wandered through the webmidi source code and might have found the problem: Line 1516 is

"octave": Math.floor(data1 / 12 - 1) - 3

I suspect that final "3" should be "1". Similar situation on lines 1550 & 1582

djipco commented 7 years ago

@cfry That's a good catch! Thanks for reporting it. There was indeed a 2 octave discrepancy between the output and input MIDI note numbers.

It should now be fixed in release 2.0.0-rc.6 (commit 25473cb). Please test it and let me know if everything now works right.

cfry commented 7 years ago

Works great, thanks.


Originally I had asked you about a "note" object. I've built a bunch of sottware that now has such a beast, and more functionality, but what would be a good change for WebMidi in keeping with its spirit, yet accommodating such a data structure?

Here's my suggestion of how it could be implemented for Output.prototype.playNote = function(note, channel, options) { if (typeof(note) == "object"){ if(channel) { throw error } else { channel = note.channel options = note note = options.pitch } } ...

Observe that the MIDI convention of using the word "note" to mean "pitch" is a disaster, so I don't recommend using it. So I'd prefer: Output.prototype.playNote = function(pitch, channel, options) { if (typeof(pitch) == "object"){ if(channel) { throw error } else { channel = pitch.channel options = pitch pitch = options.pitch } }

In any case, using this style to extend the methods that take "options" as an argument gives you the capacity to make and pass around notes as one literal JS object, and maintains backwards compatibility.

I have a class called Note. If we had the above def for playNote, I think I could just pass an instance of my Note class into playNote, and it would work. Furthermore, anyone could build their own Note-like class and as long as they used the same names and value types & ranges as your code, (which I did!) they can add whatever extra fields and methods to their class and things should work out.

On Sat, Aug 12, 2017 at 10:06 PM, Jean-Philippe Côté < notifications@github.com> wrote:

Closed #19 https://github.com/cotejp/webmidi/issues/19.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/cotejp/webmidi/issues/19#event-1203770927, or mute the thread https://github.com/notifications/unsubscribe-auth/ABITfa8xp7YrrsLqqwyoLEWqPcG_JMuzks5sXlofgaJpZM4Ou3f3 .

djipco commented 7 years ago

Ok, excellent.

P.S. As you can see, part of your response his hidden because of a separator. If you could post the bit about the Note object to a new issue, it would make it much easier to track.

aik099 commented 6 years ago

It appears, that we've changed on wrong octave detection math to another wrong one.

Right now the Middle C (midi code = 60) is shown as C3 instead of C4 in the noteon event. Same happens, when I attempt to play C3 then it actually plays C4 (Middle C).

I've reported this separately in #42.

djipco commented 6 years ago

What was corrected here is the discrepancy between the input and output note numbers. Follow up will be in #42.