ad8e / Just-intonation-keyboard

A keyboard that is always consonant
18 stars 2 forks source link

Question (choosing a key) #1

Open davidhaley opened 9 months ago

davidhaley commented 9 months ago

Good evening,

I love this project - thank you!

Is it possible (or make sense) to support choosing a 'key' (to guarantee that any ideas created are musically compatible with more traditional recordings in the same 'key')?

Aside: I am tinkering with WebMIDI (it can send notes from your keyboard over a virtual midi port to Bitwig, which is awesome).

ad8e commented 9 months ago

You can match with keys, but it requires knowing the underlying numbers for the key you're playing.

For example: if you Ctrl+F "The major scale is" on the page, you'll find the numbers for the major scale: 8 9 10 32/3 12 40/3 15 16. And it tells you the two fundamentals of that scale: 1 and 8/3. That means fundamentals (with purple/blue keys) 1 and 8/3 sound good with the major scale. 1 = the note 3 octaves below the tonal center, and 8/3 = the note 19 semitones below the tonal center (so on a C major scale, it's F). That means you look at the piano stripes underneath the keyboard, and put the "1" number at either 3 octaves below your tonal center, or 19 semitones below. ("1" is probably off the screen, so just align "2" to the key one octave above where "1" would be.)

For a first-pass understanding, that's good enough. For a little deeper understanding, you want the fundamental to divide the number. So for the C major scale, 8/3 sounds good with 8 (C), 32/3 (F), 12 (G), 40/3 (A). And 1 sounds good with 8 (C), 9 (D), 10 (E), 12 (G), 15 (B). So if your partner is playing CFGA, notes over 8/3 will generally all work. And if your partner is playing CDEGB, notes over 1 will work.

An easy way to explore this: set the fundamental to 1/8 the tonal center. If you hold down 4/3 and 2/1 on the blue keys, then you'll be in fundamental 8/3 and you can try playing keys there too. Letting go of 4/3 and 2/1 will put you back at fundamental 1.

The same thing applies to the natural minor scale: try 1, 6/5, or 8/5 as the fundamentals. For example, 8/5 would work with 8 (C), 48/5 (Eb), 12 (G), 64/5 (Ab), 72/5 (Bb). There's a sneaky property where Ab rounds between 64/5 and 63/5 depending on what notes are adjacent, but that's a bit advanced. All it means is that Ab works well with both 6/5 and 8/5.

Web MIDI would fit the keyboard; I just don't have any way to test or implement it, as I have zero MIDI devices.

davidhaley commented 9 months ago

Good evening, thank you for the prompt reply.

I apologize in advance - not having a background in music theory, I must admit that I feel a bit lost (with some of the terminology).

You can match with keys, but it requires knowing the underlying numbers for the key you're playing.

I wonder if it would make sense to expose an (optional) dropdown allowing a user to choose a key. If chosen, the appropriate ratios could be computed, and the keyboard could display "compatible" notes.


Web MIDI would fit the keyboard; I just don't have any way to test or implement it, as I have zero MIDI devices. What operating system are you using? I set up a virtual midi port, and then created a controller in Bitwig and configured it to use the virtual device.

For the current (albeit hacky) prototype, in interruptible_beep I added:

// MIDI
let midiNoteOffHandlers = [];
if (midiOutput) {
    const midiNoteNumber = frequencyToMIDINoteNumber(frequency);

    // Debug log
    console.log(frequencyToNoteAndOctave(frequency));

    // Note-on message (0x90), with the note number and max velocity (0x7F)
    midiOutput.send([0x90, midiNoteNumber, 0x7F]);

    // Pair each note-on with a corresponding note-off that will be called later
    midiNoteOffHandlers.push(() => midiOutput.send([0x80, midiNoteNumber, 0x00]));
}

Which use these GPT-generated calculation functions (I'm not positive on correctness of them):

// Frequency to MIDI note conversion
function frequencyToMIDINoteNumber(f) {
    return Math.round(12 * Math.log2(f / 440.0) + 69);
}

// Frequency to note and octave conversion
function frequencyToNoteAndOctave(f) {
    const notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
    const midiNumber = Math.round(12 * Math.log2(f / 440.0) + 69);
    const note = notes[midiNumber % 12];
    const octave = Math.floor(midiNumber / 12) - 1;
    return `${note}${octave}`;
}

The corresponding note-offs are currently called in stop_beep:

    // MIDI
    struct_of_stuff.midiNoteOffHandlers.forEach((execute) => execute());

And MIDI is requested like:

// MIDI
// Global variables to store MIDI access and output
let midiAccess = null;
let midiOutput = null;
let midiOutputs = null;
let midiInputs = null;

// Request WebMIDI access
if (navigator.requestMIDIAccess) {
    navigator.requestMIDIAccess().then(onMIDISuccess, onMIDIFailure);
}

function onMIDISuccess(midi) {
    midiAccess = midi;
    midiOutputs = Array.from(midiAccess.outputs.values());
    midiInputs = Array.from(midiAccess.inputs.values());
    console.log("MIDI success. Inputs/Outputs: ", {
        midiInputs,
        midiOutputs
    })
    const found = midiOutputs.find((output) => output.name.toLowerCase().includes("virmidi"))
    if (found) {
        midiOutput = found;
        console.log("MIDI. Active output: ", midiOutput)
    } else {
        console.log("Could not find virtual midi output")
    }

}

function onMIDIFailure() {
    console.warn("Could not access your MIDI devices.");
}

I currently hard-coded finding the virtual midi port.

I could update it to populate a dropdown of inputs/outputs, and expose a flag to enable/disable MIDI.

ad8e commented 9 months ago

By tonal center, I just mean the key that's on the scale name: so if it's G minor scale, G is the tonal center.

To set the fundamental, press purple keys to move it around. The fundamental is where "1" is, but let's look at 8 instead: this represents the key that is 8 times the fundamental. Aligning the fundamental to 1/8 the tonal center = matching "8" to the key. You accomplish this by pressing purple keys to move the fundamental around.

For example, 8 starts over E, so in the keyboard's starting state, we're ready to play notes over the E major scale. But let's say you want to play the C major scale instead. I know that the ratio of C to E is 4/5: which means that if I press the purple 4/5 button, the fundamental will shift so that "8" is now over C. Which is what happens: now "8" is over C, so I know my fundamental is 1/8 of that C key.

If you want to set your fundamental to 8/3 with respect to C major, one way is to start at 1, then press 4/3 and 2/1. Another way is to ask, "what key has frequency ratio 1/3 of the C key?" and then press purple keys until you're there. You'll know you're successful when you see the number "3" over the C key you care about. 1/3 comes about because C is 8, and you want the fundamental 8/3, so (8/3) / 8 = 1/3.

Purple keys move the fundamental permanently, so if you want to go to 1/4, you can press the purple "1/2" twice. Note that there's a safety limiter so that if you move your fundamental too far (like spamming 2/1), it'll reset you back to the starting state, where 8 is over E.

I wonder if it would make sense to expose an (optional) dropdown allowing a user to choose a key. If chosen, the appropriate ratios could be computed, and the keyboard could display "compatible" notes.

The value of such a visualization would be to allow you to change keys. If the key is fixed, there's not much to show; you just memorize 1 and 8/3 for major and 1, 6/5, 8/5 for natural minor and that's it. This rule works whether it's C major or D major or Bb major. The display would be similar to placing this list of numbers over the piano keys: "1 16/15 9/8 6/5 5/4 4/3 7/5 3/2 8/5 5/3 16/9 15/8 2", which tells you what purple buttons you need to press to get somewhere.

I'm currently on Ubuntu Linux. My impression from reading bug reports is that both Chromium and Firefox won't work with Web MIDI because both of them are enclosed in snaps, and nobody has done the work to fix this issue yet.

If you're willing to help write the MIDI interfacing/device lookups, I can actually provide the whole code (EDIT: done). I just don't have any way to test it, which is where your expertise comes in. https://webmidi-examples.glitch.me/ looks quite simple but I have not gotten it to run on any of my 4 devices.

It'll output 12ET. If you want to output Just Intonation, I can also pass the exact frequencies to do pitch bends with. I don't know which is best here, because that's related to MIDI compatibility, which I assume you know a lot more about than I do.

davidhaley commented 9 months ago

By tonal center, I just mean the key that's on the scale name: so if it's G minor scale, G is the tonal center.

Oh, I understand now - thank you.

I really appreciate the detailed explanation on all of the above. I'll have more time later this week to revisit the keyboard and apply your advice while experimenting. I look forward to it.

I'm currently on Ubuntu Linux. My impression from reading bug reports is that both Chromium and Firefox won't work with Web MIDI because both of them are enclosed in snaps, and nobody has done the work to fix this issue yet.

Oh, that's unfortunate.

If you're willing to help write the MIDI interfacing/device lookups (EDIT: done)

That was quick! I replied to the other GH issue.

Yes, I can help out.

If you want to output Just Intonation, I can also pass the exact frequencies to do pitch bends with

This is an interesting idea. Since we have raw frequencies, and (I believe) MIDI is 7 bit, it might be interesting to explore the OSC (Open Sound Control) protocol (32/64 bit float). Although, I have never used OSC before, I've heard of it, so the thought came to mind.

ad8e commented 9 months ago

OSC looks interesting, but I think browsers only support Web MIDI for now. Anyway, the only point in implementing something is if you want it - you're the only user and tester :)

MPE and MIDI 2.0 both support exact pitch. Midi 2.0 isn't yet implemented for Web MIDI. MPE appears complicated and I'm not looking forward to it.

If you're playing over existing music, you won't want Just Intonation here. Both 12ET and Just Intonation sound good on their own, but mixing them produces unpleasant commas.