All files / src/lib/compiler/sounds compileWav.ts

10.53% Statements 6/57
0% Branches 0/18
0% Functions 0/2
9.43% Lines 5/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 1042x 2x 2x 2x                   2x                                                                                                                                                                                    
import { readFile } from "fs-extra";
import { decHexVal } from "shared/lib/helpers/8bit";
import clamp from "shared/lib/helpers/clamp";
import { WaveFile } from "wavefile";
 
type CompileOutputFmt = "c" | "asm";
 
type WaveFileFmt = {
  numChannels: number;
  bitsPerSample: number;
  sampleRate: number;
};
 
export const compileWav = async (
  filename: string,
  fmt: CompileOutputFmt = "c"
): Promise<string> => {
  const decHex = (v: number, fmt?: CompileOutputFmt) => {
    const prefix = fmt === "asm" ? "$" : "0x";
    return `${prefix}${decHexVal(v)}`;
  };
  const binPrefix = fmt === "asm" ? "%" : "0b";
 
  const file = await readFile(filename);
 
  const wav = new WaveFile(file);
 
  let wavFmt = wav.fmt as WaveFileFmt;
 
  // const isUncompressed = (p.comptype == 'NONE')
  const isUncompressed = true;
 
  // Resample is sample rate is wrong
  Iif (wavFmt.sampleRate < 8000 || wavFmt.sampleRate > 8192) {
    wav.toSampleRate(8000);
    wavFmt = wav.fmt as WaveFileFmt;
  }
 
  // Convert to 8bit if not already
  Iif (wavFmt.bitsPerSample !== 8) {
    wav.toBitDepth("8");
    wavFmt = wav.fmt as WaveFileFmt;
  }
 
  Iif (
    // wavFmt.numChannels !== 1 ||
    // wavFmt.bitsPerSample !== 8 ||
    wavFmt.sampleRate < 8000 ||
    wavFmt.sampleRate > 8192 ||
    !isUncompressed
  ) {
    throw new Error("Unsupport wav");
  }
 
  //   const rawData: Float64Array = wav.getSamples(true);
  let data: Float64Array = wav.getSamples(true);
 
  // Merge multi channel wavs
  Iif (wavFmt.numChannels > 1) {
    const newLength = Math.floor(data.length / wavFmt.numChannels);
    const newData = new Float64Array(newLength);
    let ii = 0;
    for (let i = 0; i < newLength; i++) {
      let newVal = 0;
      for (let j = 0; j < wavFmt.numChannels; j++) {
        newVal += data[ii + j] / wavFmt.numChannels;
      }
      newData[i] = clamp(Math.round(newVal), 0, 255);
      ii += wavFmt.numChannels;
    }
    data = newData;
  }
 
  let result = "";
  let output = "";
 
  const dataLength = data.length - (data.length % 32);
  let c = 0;
  let cnt = 0;
  let flag = false;
 
  for (let i = 0; i < dataLength; i++) {
    //
    c = ((c << 4) | (data[i] >> 4)) & 0xff;
    Iif (flag) {
      result += decHex(c, fmt); //sEMIT.format(c);
      cnt += 1;
      if (cnt % 16 === 0) {
        result = `${decHex(1, fmt)},${binPrefix}00000110,${result},`;
        // outf.write(bytes(result, "ascii"));
        Iif (fmt === "c") {
          result += "\n";
        }
        output += result;
        result = "";
      } else {
        result += ",";
      }
    }
    flag = !flag;
  }
  return `${output}${decHex(1, fmt)},${binPrefix}00000111`;
};