All files / src/components/music/piano helpers.ts

0% Statements 0/71
0% Branches 0/10
0% Functions 0/15
0% Lines 0/53

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122                                                                                                                                                                                                                                                   
import { PatternCell } from "shared/lib/uge/types";
import {
  PIANO_ROLL_CELL_SIZE,
  TOTAL_NOTES,
  TRACKER_PATTERN_LENGTH,
} from "consts";
 
export const calculatePlaybackTrackerPosition = (
  playbackOrder: number,
  playbackRow: number,
) =>
  playbackOrder * TRACKER_PATTERN_LENGTH * PIANO_ROLL_CELL_SIZE +
  playbackRow * PIANO_ROLL_CELL_SIZE;
 
export const calculateDocumentWidth = (sequenceLength: number) =>
  sequenceLength * TRACKER_PATTERN_LENGTH * PIANO_ROLL_CELL_SIZE;
 
export const noteToRow = (note: number) => TOTAL_NOTES - 1 - note;
 
export const rowToNote = (row: number) => TOTAL_NOTES - 1 - row;
 
export const wrapNote = (note: number) =>
  ((note % TOTAL_NOTES) + TOTAL_NOTES) % TOTAL_NOTES;
 
export interface AbsColPosition {
  sequenceId: number;
  column: number;
}
 
export const toAbsCol = (sequenceId: number, column: number) =>
  sequenceId * TRACKER_PATTERN_LENGTH + column;
 
export const fromAbsCol = (absCol: number): AbsColPosition => ({
  sequenceId: Math.floor(absCol / TRACKER_PATTERN_LENGTH),
  column: absCol % TRACKER_PATTERN_LENGTH,
});
 
export interface ResolvedAbsCol extends AbsColPosition {
  patternId: number;
}
 
export const resolveAbsCol = (
  sequence: number[],
  absCol: number,
): ResolvedAbsCol | null => {
  const { sequenceId, column } = fromAbsCol(absCol);
  const patternId = sequence[sequenceId];
  Iif (patternId === undefined) {
    return null;
  }
  return { sequenceId, column, patternId };
};
 
export const clonePattern = (pattern: PatternCell[][]) =>
  pattern.map((column) => column.map((cell) => ({ ...cell })));
 
export const clonePatterns = (patterns: PatternCell[][][]) =>
  patterns.map(clonePattern);
 
export const mutatePatternsAndCollectChanges = (
  sourcePatterns: PatternCell[][][],
  mutate: (
    clonedPatterns: PatternCell[][][],
    changedPatternIds: Set<number>,
  ) => void,
) => {
  const clonedPatterns = clonePatterns(sourcePatterns);
  const changedPatternIds = new Set<number>();
  mutate(clonedPatterns, changedPatternIds);
  return { clonedPatterns, changedPatternIds };
};
 
export const commitChangedPatterns = (
  changedPatternIds: Iterable<number>,
  clonedPatterns: PatternCell[][][],
  commit: (patternId: number, pattern: PatternCell[][]) => void,
) => {
  for (const patternId of changedPatternIds) {
    commit(patternId, clonedPatterns[patternId]);
  }
};
 
export interface RollGridPoint {
  absCol: number;
  note: number;
}
 
export const interpolateGridLine = (
  from: RollGridPoint | null,
  to: RollGridPoint,
): RollGridPoint[] => {
  Iif (!from) {
    return [to];
  }
 
  const points: RollGridPoint[] = [];
  let x0 = from.absCol;
  let y0 = from.note;
  const x1 = to.absCol;
  const y1 = to.note;
  const dx = Math.abs(x1 - x0);
  const dy = Math.abs(y1 - y0);
  const sx = x0 < x1 ? 1 : -1;
  const sy = y0 < y1 ? 1 : -1;
  let err = dx - dy;
 
  while (x0 !== x1 || y0 !== y1) {
    const e2 = 2 * err;
    Iif (e2 > -dy) {
      err -= dy;
      x0 += sx;
    }
    Iif (e2 < dx) {
      err += dx;
      y0 += sy;
    }
    points.push({ absCol: x0, note: y0 });
  }
 
  return points;
};