/ proof-of-concept / midiboot
Sign in
proof-of-concept / midiboot
midiboot / src / types.ts
87 lines · 1.9 KBCodeBlameHistory
0ad1ec1midiboot: in-browser MIDI workstation with pluggable instrumentsJeremy Magland 1// Core song model. Time is stored in MIDI ticks against the song's ppq
2// (pulses per quarter note); the tempo map converts to seconds.
4export interface Note {
5 id: string;
6 tick: number; // start, in ticks
7 duration: number; // in ticks, >= 1
8 pitch: number; // MIDI note number 0..127
9 velocity: number; // 1..127
12export interface Track {
13 id: string;
14 name: string;
15 instrumentId: string;
16 volume: number; // 0..1.5, applied at mix time
17 mute: boolean;
18 solo: boolean;
19 notes: Note[];
22export interface TempoEvent {
23 tick: number;
24 usPerQuarter: number; // microseconds per quarter note
27export interface TimeSigEvent {
28 tick: number;
29 numerator: number;
30 denominator: number; // 4 = quarter gets the beat
33export interface Song {
34 ppq: number;
35 tempo: TempoEvent[]; // sorted by tick, first at tick 0
36 timeSig: TimeSigEvent[]; // sorted by tick, first at tick 0
37 tracks: Track[];
40export interface SongRecord {
41 id: string;
42 name: string;
43 createdAt: number;
44 updatedAt: number;
45 song: Song;
48export interface SongMeta {
49 id: string;
50 name: string;
51 updatedAt: number;
52 trackCount: number;
53 noteCount: number;
56let idCounter = 0;
57export function newId(prefix = 'x'): string {
58 idCounter += 1;
59 return `${prefix}${Date.now().toString(36)}${idCounter.toString(36)}${Math.floor(Math.random() * 46656).toString(36)}`;
62export const DEFAULT_PPQ = 480;
64export function defaultSong(): Song {
65 return {
66 ppq: DEFAULT_PPQ,
67 tempo: [{ tick: 0, usPerQuarter: 500000 }], // 120 bpm
68 timeSig: [{ tick: 0, numerator: 4, denominator: 4 }],
69 tracks: [
70 {
71 id: newId('t'),
72 name: 'Track 1',
73 instrumentId: 'pluck',
74 volume: 1,
75 mute: false,
76 solo: false,
77 notes: [],
78 },
79 ],
80 };
83export function songEndTick(song: Song): number {
84 let end = 0;
85 for (const t of song.tracks) for (const n of t.notes) end = Math.max(end, n.tick + n.duration);
86 return end;
↑↓ move↵openescclose