Skip to content

Commit

Permalink
commit
Browse files Browse the repository at this point in the history
  • Loading branch information
vincerubinetti committed Jun 5, 2023
1 parent 7fdbbec commit e767e77
Show file tree
Hide file tree
Showing 11 changed files with 64 additions and 27 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ A tool to randomize note starts, durations, tempo drift, and velocities in a MID

[⭐️ Open the App ⭐️](https://vincerubinetti.github.io/midi-humanizer/)

<img width="300" src="https://raw.githubusercontent.com/vincerubinetti/midi-humanizer/main/public/screenshot.jpg?raw=true" />

When writing music in a [DAW](https://en.wikipedia.org/wiki/Digital_audio_workstation), you often sequence in notes perfectly snapped to the grid.
Sometimes you might want to add some randomness to make it sound more like a human played them.
Unfortunately, many DAWs provide little or no controls to do this automatically.
Expand Down
11 changes: 11 additions & 0 deletions public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/sample.mid
Binary file not shown.
Binary file added public/screenshot.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/test.mid
Binary file not shown.
7 changes: 6 additions & 1 deletion src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
--off-white: #eceff1;
--light-gray: #cfd8dc;
--gray: #90a4ae;
--mid-gray: #607d8b;
--dark-gray: #263238;
--black: #000000;

Expand Down Expand Up @@ -59,7 +60,9 @@ footer {
}

h1 {
display: inline-block;
display: inline-flex;
gap: 20px;
align-items: center;
margin: 0;
font: inherit;
font-size: 2rem;
Expand All @@ -68,6 +71,7 @@ h1 {
section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
align-items: flex-start;
gap: 40px;
width: 100%;
}
Expand Down Expand Up @@ -97,6 +101,7 @@ a:hover {
.control-label {
min-width: 80px;
padding-left: 10px;
color: var(--mid-gray);
}

.control-primary {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Group.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
.group legend {
position: absolute;
bottom: calc(100% + 10px);
color: var(--dark-gray);
color: var(--mid-gray);
}
2 changes: 2 additions & 0 deletions src/sections/File.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DragEventHandler, useEffect, useRef, useState } from "react";
import { useAtom } from "jotai";
import { startCase } from "lodash";
import { Midi } from "@tonejs/midi";
import Button from "@/components/Button";
import Group from "@/components/Group";
Expand Down Expand Up @@ -30,6 +31,7 @@ const File = () => {
console.info(midi);

/** set state data */
if (!midi.name) midi.name = startCase(file.name.replace(/\.midi?$/, ""));
setMidi(midi);
setFilename(file.name);

Expand Down
5 changes: 4 additions & 1 deletion src/sections/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const Header = () => (
<header>
<h1>MIDI Humanizer</h1>
<h1>
<img src="logo.svg" style={{ height: "1em" }} alt="" />
MIDI Humanizer
</h1>
</header>
);

Expand Down
21 changes: 14 additions & 7 deletions src/sections/Track.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,25 @@ const Info = () => {
if (!getMidi) return <></>;

/** list of instruments */
const instruments =
getMidi.tracks.map((track) => {
const { name, family, percussion } = track.instrument;
return [name, family, percussion ? "percussion" : ""]
const instruments = getMidi.tracks.map(
(track, index) =>
index +
1 +
". " +
[
track.name,
track.instrument.name,
track.instrument.family,
track.instrument.percussion ? "percussion" : "",
]
.filter(Boolean)
.join(" | ");
}) || [];
.join(" | ")
);

return (
<Group label="Track">
<Select
label="Select"
label="View"
options={instruments}
value={getTrack}
onChange={setTrack}
Expand Down
41 changes: 24 additions & 17 deletions src/state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ export const midi = atom<Midi | null>(null);
export const track = atom(0);
export const filename = atom("");
export const pitchRange = atom((get) => {
/** get current track */
const currentTrack = get(midi)?.tracks[get(track)];

/** find min/max pitches */
let min;
let max;

/** find min/max pitches of all tracks */
for (const track of get(midi)?.tracks || []) {
for (const note of track.notes) {
if (!min || note.midi < min) min = note.midi;
if (!max || note.midi > max) max = note.midi;
}
for (const note of currentTrack?.notes || []) {
if (!min || note.midi < min) min = note.midi;
if (!max || note.midi > max) max = note.midi;
}

/** extend one octave in each direction */
Expand Down Expand Up @@ -125,7 +125,8 @@ export const humanized = atom((get) => {
/** go through each node */
for (const [index, note] of Object.entries(track.notes)) {
/** start time */
note.ticks +=
note.ticks =
note.ticks +
/** random spread */
random("start" + index + get(start).seed) * get(start).randomness +
/** random drift */
Expand All @@ -134,19 +135,21 @@ export const humanized = atom((get) => {
get(start).shift;

/** duration */
note.durationTicks +=
/** random spread */
(random("duration" + index + get(duration).seed) *
get(duration).randomness +
note.durationTicks =
(note.durationTicks +
/** random spread */
random("duration" + index + get(duration).seed) *
get(duration).randomness +
/** transform */
get(duration).shift) *
get(duration).scale;

/** randomize velocity */
note.velocity +=
note.velocity =
/** random spread */
(random("velocity" + index + get(velocity).seed) *
get(velocity).randomness +
(note.velocity +
random("velocity" + index + get(velocity).seed) *
get(velocity).randomness +
/** transform */
get(velocity).shift) *
get(velocity).scale;
Expand Down Expand Up @@ -209,5 +212,9 @@ export const incSeed = () => {
store.set(velocity, (value) => ({ ...value, seed: value.seed + 1 }));
};

/** for testing */
// Midi.fromUrl("test.mid").then((parsed) => store.set(midi, parsed));
/** load sample */
Midi.fromUrl("sample.mid").then((parsed) => {
console.info(parsed);
store.set(filename, "sample.mid");
store.set(midi, parsed);
});

0 comments on commit e767e77

Please sign in to comment.