| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { Midi } from "@tonejs/midi"; |
| import { |
| LIMB_TO_GM, |
| type Limb, |
| type MidiEvent, |
| } from "@patterntalk/shared-types"; |
| import { bpmToMicrosecondsPerQuarter } from "../patterns/engine"; |
|
|
| export interface MidiMeta { |
| patternId: string; |
| patternName: string; |
| bars: number; |
| bpm: number; |
| } |
|
|
| |
| |
| |
| |
| export function eventsToMidi(events: MidiEvent[], meta: MidiMeta): Uint8Array { |
| const midi = new Midi(); |
|
|
| |
| midi.header.setTempo(bpmToMicrosecondsPerQuarter(meta.bpm)); |
| midi.header.timeSignatures = [{ ticks: 0, timeSignature: [4, 4] }]; |
|
|
| const drums = midi.addTrack(); |
| drums.name = `${meta.patternName} (Jam Buddy)`; |
| drums.channel = 9; |
|
|
| for (const ev of events) { |
| const midiNote = LIMB_TO_GM[ev.limb]; |
| drums.addNote({ |
| midi: midiNote, |
| ticks: ev.tick, |
| durationTicks: ev.duration, |
| velocity: ev.velocity / 127, |
| }); |
| } |
|
|
| return midi.toArray(); |
| } |
|
|
| |
| |
| |
| |
| export function midiFilename(meta: MidiMeta, when: Date = new Date()): string { |
| const ts = when.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z"); |
| return `jambuddy-${meta.patternId}-${meta.bars}bars-${meta.bpm}bpm-${ts}.mid`; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function downloadMidi(events: MidiEvent[], meta: MidiMeta): string { |
| const bytes = eventsToMidi(events, meta); |
| |
| |
| const blob = new Blob([new Uint8Array(bytes)], { type: "audio/midi" }); |
| const url = URL.createObjectURL(blob); |
| const filename = midiFilename(meta); |
|
|
| const a = document.createElement("a"); |
| a.href = url; |
| a.download = filename; |
| a.rel = "noopener"; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
|
|
| |
| setTimeout(() => URL.revokeObjectURL(url), 1000); |
|
|
| return filename; |
| } |
|
|
| |
| |
| |
| |
| |
| export function midiAsFile(events: MidiEvent[], meta: MidiMeta): File { |
| const bytes = eventsToMidi(events, meta); |
| return new File( |
| [new Uint8Array(bytes)], |
| midiFilename(meta), |
| { type: "audio/midi" }, |
| ); |
| } |
|
|
| |
| export type { Limb }; |
|
|