Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move MIDI classes to a separate package #366

Merged
merged 1 commit into from
Nov 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 36 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
"temperaments": "^0.4.5",
"vue": "^3.2.33",
"vue-router": "^4.1.5",
"webmidi": "^3.0.21",
"xen-dev-utils": "^0.1.4"
"webmidi": "^3.1.7",
"xen-dev-utils": "^0.1.4",
"xen-midi": "^0.1.0"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
Expand Down
46 changes: 17 additions & 29 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,8 @@ import {
} from "@/constants";
import { ScaleWorkshopOneData } from "@/scale-workshop-one";
import type { Input, Output } from "webmidi";
import {
bendRangeInSemitones,
computeWhiteIndices,
MidiIn,
midiNoteInfo,
MidiOut,
} from "@/midi";
import { computeWhiteIndices } from "@/midi";
import { MidiIn, midiKeyInfo, MidiOut } from "xen-midi";
import { Keyboard, type CoordinateKeyboardEvent } from "@/keyboard";
import { decodeQuery, encodeQuery, type DecodedState } from "@/url-encode";
import { debounce } from "@/utils";
Expand Down Expand Up @@ -349,9 +344,9 @@ function getFrequency(index: number) {
}
}

const midiOut = computed(() => {
return new MidiOut(midiOutput.value as Output, midiOutputChannels.value);
});
const midiOut = computed(
() => new MidiOut(midiOutput.value as Output, midiOutputChannels.value)
);

function sendNoteOn(frequency: number, rawAttack: number) {
const midiOff = midiOut.value.sendNoteOn(frequency, rawAttack);
Expand Down Expand Up @@ -394,14 +389,17 @@ function sendNoteOn(frequency: number, rawAttack: number) {
// Offset such that default base MIDI note doesn't move
const WHITE_MODE_OFFSET = 69 - 40;

function midiNoteOn(index: number, rawAttack: number) {
function midiNoteOn(index: number, rawAttack?: number) {
if (rawAttack === undefined) {
rawAttack = 80;
}
let frequency = frequencies.value[index];
if (!midiVelocityOn.value) {
rawAttack = 80;
}

// Store state to ensure consistent note off.
const info = midiNoteInfo(index);
const info = midiKeyInfo(index);
const whiteMode = midiWhiteMode.value;
const indices = whiteIndices.value;

Expand Down Expand Up @@ -453,11 +451,14 @@ function midiNoteOn(index: number, rawAttack: number) {

if (isNaN(frequency)) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return (rawRelease: number) => {};
return (rawRelease?: number) => {};
}

const noteOff = sendNoteOn(frequency, rawAttack);
return (rawRelease: number) => {
return (rawRelease?: number) => {
if (rawRelease === undefined) {
rawRelease = 80;
}
if (!midiVelocityOn.value) {
rawRelease = 80;
}
Expand Down Expand Up @@ -485,11 +486,10 @@ const RESERVED_MESSAGES = ["noteon", "noteoff", "pitchbend"];

watch(midiInput, (newValue, oldValue) => {
if (oldValue !== null) {
oldValue.removeListener();
midiIn.unlisten(oldValue as Input);
}
if (newValue !== null) {
newValue.addListener("noteon", midiIn.noteOn.bind(midiIn));
newValue.addListener("noteoff", midiIn.noteOff.bind(midiIn));
midiIn.listen(newValue as Input);

// Pass everything else through and distrubute among the channels
newValue.addListener("midimessage", (event) => {
Expand All @@ -515,15 +515,6 @@ watch(midiInput, (newValue, oldValue) => {
}
});

function sendPitchBendRange() {
const output = midiOutput.value;
if (output !== null) {
midiOutputChannels.value.forEach((channel) => {
output.channels[channel].sendPitchBendRange(bendRangeInSemitones, 0);
});
}
}

// === Virtual and typing keyboard ===
function keyboardNoteOn(index: number) {
tuningTableKeyOn(index);
Expand Down Expand Up @@ -812,9 +803,6 @@ watch(mainVolume, (newValue) => {
);
});

watch(midiOutput, sendPitchBendRange);
watch(midiOutputChannels, sendPitchBendRange);

function updateMidiInputChannels(newValue: Set<number>) {
midiInputChannels.clear();
newValue.forEach((channel) => midiInputChannels.add(channel));
Expand Down
7 changes: 4 additions & 3 deletions src/__tests__/midi.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, it, expect } from "vitest";
import { midiKeyInfo } from "xen-midi";

import { computeWhiteIndices, midiNoteInfo } from "../midi";
import { computeWhiteIndices } from "../midi";

describe("White key to white color mapper", () => {
it("reproduces a chromatic scale in the default (A minor) configuration", () => {
Expand All @@ -20,7 +21,7 @@ describe("White key to white color mapper", () => {
]);

for (let index = 69; index < 69 + 12; index++) {
const info = midiNoteInfo(index);
const info = midiKeyInfo(index);
if (info.whiteNumber === undefined) {
expect(map[info.sharpOf] + 1).toBe(index);
} else {
Expand All @@ -46,7 +47,7 @@ describe("White key to white color mapper", () => {
]);

for (let index = 60; index < 60 + 12; index++) {
const info = midiNoteInfo(index);
const info = midiKeyInfo(index);
if (info.whiteNumber === undefined) {
expect(map[info.sharpOf] + 1).toBe(index);
} else {
Expand Down
Loading