• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

semperai / vad-ts / 18363907063

09 Oct 2025 02:44AM UTC coverage: 73.936%. First build
18363907063

Pull #1

github

web-flow
Merge 79ce1f1ec into b562b936a
Pull Request #1: Typescript Port

187 of 199 branches covered (93.97%)

Branch coverage included in aggregate %.

460 of 637 new or added lines in 8 files covered. (72.21%)

1098 of 1539 relevant lines covered (71.35%)

893.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.17
/packages/web/src/utils.ts
1
export function minFramesForTargetMS(
1✔
2
  targetDuration: number,
14✔
3
  frameSamples: number,
14✔
4
  sr = 16000
14✔
5
): number {
14✔
6
  return Math.ceil((targetDuration * sr) / 1000 / frameSamples)
14✔
7
}
14✔
8

9
export function arrayBufferToBase64(buffer: ArrayBuffer) {
1✔
10
  const bytes = new Uint8Array(buffer)
7✔
11
  const len = bytes.byteLength
7✔
12
  const binary = new Array(len)
7✔
13
  for (var i = 0; i < len; i++) {
7✔
14
    const byte = bytes[i]
98✔
15
    if (byte === undefined) {
98!
16
      break
×
17
    }
×
18
    binary[i] = String.fromCharCode(byte)
98✔
19
  }
98✔
20
  return btoa(binary.join(""))
7✔
21
}
7✔
22

23
/*
24
This rest of this was mostly copied from https://github.com/linto-ai/WebVoiceSDK
25
*/
26

27
export function encodeWAV(
1✔
28
  samples: Float32Array,
16✔
29
  format: number = 3,
16✔
30
  sampleRate: number = 16000,
16✔
31
  numChannels: number = 1,
16✔
32
  bitDepth: number = 32
16✔
33
) {
16✔
34
  var bytesPerSample = bitDepth / 8
16✔
35
  var blockAlign = numChannels * bytesPerSample
16✔
36
  var buffer = new ArrayBuffer(44 + samples.length * bytesPerSample)
16✔
37
  var view = new DataView(buffer)
16✔
38
  /* RIFF identifier */
39
  writeString(view, 0, "RIFF")
16✔
40
  /* RIFF chunk length */
41
  view.setUint32(4, 36 + samples.length * bytesPerSample, true)
16✔
42
  /* RIFF type */
43
  writeString(view, 8, "WAVE")
16✔
44
  /* format chunk identifier */
45
  writeString(view, 12, "fmt ")
16✔
46
  /* format chunk length */
47
  view.setUint32(16, 16, true)
16✔
48
  /* sample format (raw) */
49
  view.setUint16(20, format, true)
16✔
50
  /* channel count */
51
  view.setUint16(22, numChannels, true)
16✔
52
  /* sample rate */
53
  view.setUint32(24, sampleRate, true)
16✔
54
  /* byte rate (sample rate * block align) */
55
  view.setUint32(28, sampleRate * blockAlign, true)
16✔
56
  /* block align (channel count * bytes per sample) */
57
  view.setUint16(32, blockAlign, true)
16✔
58
  /* bits per sample */
59
  view.setUint16(34, bitDepth, true)
16✔
60
  /* data chunk identifier */
61
  writeString(view, 36, "data")
16✔
62
  /* data chunk length */
63
  view.setUint32(40, samples.length * bytesPerSample, true)
16✔
64
  if (format === 1) {
16✔
65
    // Raw PCM
66
    floatTo16BitPCM(view, 44, samples)
1✔
67
  } else {
16✔
68
    writeFloat32(view, 44, samples)
15✔
69
  }
15✔
70
  return buffer
16✔
71
}
16✔
72

73
function writeFloat32(output: DataView, offset: number, input: Float32Array) {
15✔
74
  for (var i = 0; i < input.length; i++, offset += 4) {
15✔
75
    output.setFloat32(offset, input[i] as number, true)
16,166✔
76
  }
16,166✔
77
}
15✔
78

79
function floatTo16BitPCM(
1✔
80
  output: DataView,
1✔
81
  offset: number,
1✔
82
  input: Float32Array
1✔
83
) {
1✔
84
  for (var i = 0; i < input.length; i++, offset += 2) {
1✔
85
    var s = Math.max(-1, Math.min(1, input[i] as number))
2✔
86
    output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true)
2✔
87
  }
2✔
88
}
1✔
89

90
function writeString(view: DataView, offset: number, string: string) {
64✔
91
  for (var i = 0; i < string.length; i++) {
64✔
92
    view.setUint8(offset + i, string.charCodeAt(i))
256✔
93
  }
256✔
94
}
64✔
95

96
export async function audioFileToArray(audioFileData: Blob) {
×
97
  const ctx = new OfflineAudioContext(1, 1, 44100)
×
98
  const reader = new FileReader()
×
99
  let audioBuffer: AudioBuffer | null = null
×
NEW
100
  await new Promise<void>((resolve, reject) => {
×
NEW
101
    reader.addEventListener("loadend", () => {
×
102
      const audioData = reader.result as ArrayBuffer
×
NEW
103
      ctx
×
NEW
104
        .decodeAudioData(audioData.slice(0))
×
NEW
105
        .then((buffer) => {
×
106
          audioBuffer = buffer
×
NEW
107
          resolve()
×
NEW
108
        })
×
NEW
109
        .catch((error) => {
×
NEW
110
          reject(new Error(`Failed to decode audio data: ${error}`))
×
NEW
111
        })
×
112
    })
×
113
    reader.readAsArrayBuffer(audioFileData)
×
114
  })
×
115
  if (audioBuffer === null) {
×
116
    throw Error("some shit")
×
117
  }
×
118
  let _audioBuffer = audioBuffer as AudioBuffer
×
119
  let out = new Float32Array(_audioBuffer.length)
×
NEW
120
  const channelCount = _audioBuffer.numberOfChannels
×
121
  for (let i = 0; i < _audioBuffer.length; i++) {
×
NEW
122
    let sample = 0
×
NEW
123
    for (let j = 0; j < channelCount; j++) {
×
NEW
124
      sample += _audioBuffer.getChannelData(j)[i] ?? 0
×
125
    }
×
NEW
126
    out[i] = sample / channelCount
×
127
  }
×
128
  return { audio: out, sampleRate: _audioBuffer.sampleRate }
×
129
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc