Skip to content

Commit

Permalink
Add MTS pitch and SysEx format conversion methods
Browse files Browse the repository at this point in the history
  • Loading branch information
vsicurella committed Jun 18, 2023
1 parent 4c8e0be commit c75624f
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 1 deletion.
30 changes: 30 additions & 0 deletions src/__tests__/conversion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import {
centOffsetToFrequency,
centsToValue,
frequencyToCentOffset,
ftomts,
frequencyToMtsBytes,
ftom,
mtof,
valueToCents,
mtsBytesToHex
} from '../conversion';

describe('Ratio to cents converter', () => {
Expand Down Expand Up @@ -38,6 +41,33 @@ describe('MIDI to frequency converter', () => {
});
});

describe('Frequency to MTS converter', () => {
it('converts a known value', () => {
expect(ftomts(261.625565)).toBeCloseTo(60);
expect(ftomts(0, false)).toBe(0);
expect(ftomts(14080, false)).toBe(127);
expect(ftomts(14080, true)).toBeCloseTo(129);
})
})

describe("Frequency to MTS sysex value", () => {
it('converts a known value', () => {
expect(frequencyToMtsBytes(261.625565)).toMatchObject(new Uint8Array([60, 0, 0]));
expect(frequencyToMtsBytes(440)).toMatchObject(new Uint8Array([69, 0, 0]));
expect(frequencyToMtsBytes(442)).toMatchObject(new Uint8Array([69, 10, 6]));
expect(frequencyToMtsBytes(0)).toMatchObject(new Uint8Array([0, 0, 0]));
})
})

describe("MTS data hex string converter", () => {
it('converts a known value', () => {
expect(mtsBytesToHex(new Uint8Array([60, 0, 0]))).toEqual("3c0000");
expect(mtsBytesToHex(new Uint8Array([69, 0, 0]))).toEqual("450000");
expect(mtsBytesToHex(new Uint8Array([69, 10, 6]))).toEqual("450a06");
expect(mtsBytesToHex(new Uint8Array([69, 256, 6]))).toEqual("450006");
})
})

describe('Frequency to MIDI converter', () => {
it('converts a known value', () => {
const [index, offset] = ftom(261.625565);
Expand Down
80 changes: 79 additions & 1 deletion src/conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,30 @@ export function centOffsetToFrequency(offset: number, baseFrequency = 440) {
const MIDI_NOTE_NUMBER_OF_A4 = 69;
/**
* Convert MIDI note number to frequency.
* @param index MIDI note number.
* @param index MIDI note number or MTS value.
* @returns Frequency in Hertz.
*/
export function mtof(index: number) {
return 440 * Math.pow(2, (index - MIDI_NOTE_NUMBER_OF_A4) / 12);
}

function mtsRound(mtsValue: number): number {
return Math.round(mtsValue * 100000) / 100000;
}
/**
* Convert frequency to MTS number (semitones with A440=69).
* @param frequency Frequency in Hertz.
* @returns MTS value
*/
export function ftomts(frequency: number, ignoreLimit: boolean=false): number {
const mts = mtsRound(MIDI_NOTE_NUMBER_OF_A4 + 12 * Math.log2(frequency / 440));
if (mts < 0 && !ignoreLimit)
return 0;
if (mts > 127.9999 && !ignoreLimit) // Last accepted value - 7F 7F 7F is reserved
return 127.9999;
return mts;
}

/**
* Convert frequency to MIDI note number and pitch offset measured in cents.
* @param frequency Frequency in Hertz.
Expand All @@ -70,6 +87,67 @@ export function ftom(frequency: number): [number, number] {
return [midiNoteNumber, centsOffset];
}

/**
* Convert MTS pitch value to 3-byte representation
* @param number MTS pitch value
* @returns Uint8Array 3-byte of 7-bit MTS data
*/
export function mtsToMtsBytes(mtsValue: number): Uint8Array {
const noteNumber = Math.trunc(mtsValue);
const fine = Math.round(0x3fff * (mtsValue - noteNumber));

const data = new Uint8Array(3);
data[0] = noteNumber;
data[1] = (fine >> 7) & 0x7f;
data[2] = fine & 0x7f;
return data;
}

/**
* Convert frequency to 3-byte MTS value
* @param frequency Frequency in Hertz.
* @returns Uint8Array of length 3
*/
export function frequencyToMtsBytes(frequency: number): Uint8Array {
const mtsValue = ftomts(frequency);
return mtsToMtsBytes(mtsValue);
}

/**
* Convert 3-byte MTS value to frequency
* @param Uint8Array of 3-bytes of 7-bit MTS values
* @returns frequency Frequency in Hertz
*/
export function mtsBytesToMts(mtsBytes: Uint8Array): number {
const noteNumber = mtsBytes[0];

const high = (mtsBytes[1] & 0x7f) << 7;
const low = mtsBytes[2] & 0x7f;
const fine = (high + low) / 0x3fff;
return noteNumber + fine;
}

/**
* Convert 3-byte MTS value to frequency
* @param Uint8Array of 3-bytes of 7-bit MTS values
* @returns frequency Frequency in Hertz
*/
export function mtsBytesToFrequency(mtsBytes: Uint8Array): number {
const mts = mtsBytesToMts(mtsBytes);
return mtof(mts);
}

/** Convert MTS Data value into readable hex string
* @param Uint8Array of 3-bytes of 7-bit MTS values
* @returns String representation of MTS value in hexadecimal
* can be used in MIDI messages
*/
export function mtsBytesToHex(mtsBytes: Uint8Array): String {
return (mtsBytes[0] & 0x7f).toString(16).padStart(2, "0")
+ (mtsBytes[1] & 0x7f).toString(16).padStart(2, "0")
+ (mtsBytes[2] & 0x7f).toString(16).padStart(2, "0");
}

/**
* Convert cents to natural units.
* @param cents Musical interval in cents.
Expand Down

0 comments on commit c75624f

Please sign in to comment.