diff --git a/resources/js/lib/voice-notes.ts b/resources/js/lib/voice-notes.ts index 1b8448dcb..6c8a0906a 100644 --- a/resources/js/lib/voice-notes.ts +++ b/resources/js/lib/voice-notes.ts @@ -88,15 +88,23 @@ export function waveformOf(samples: Float32Array, bars = voiceBars): number[] { */ export function barsOf(levels: number[], bars = voiceBars): number[] { return Array.from({ length: bars }, (_, bar) => { + if (levels.length === 0) { + return 0; + } + const from = Math.floor((levels.length * bar) / bars); const to = Math.max( from + 1, Math.floor((levels.length * (bar + 1)) / bars), ); - return Math.round( - Math.min(1, Math.max(0, ...levels.slice(from, to))) * 100, - ); + let peak = 0; + + for (let index = from; index < to; index++) { + peak = Math.max(peak, levels[index] as number); + } + + return Math.round(Math.min(1, peak) * 100); }); } diff --git a/tests/Unit/VoiceNotes.test.ts b/tests/Unit/VoiceNotes.test.ts index c1e936563..468a46af3 100644 --- a/tests/Unit/VoiceNotes.test.ts +++ b/tests/Unit/VoiceNotes.test.ts @@ -85,3 +85,23 @@ void test('reads the saved volume as a whole percent, at full volume until a sen assert.equal(voiceVolumeOf('-0.3'), 0); assert.equal(voiceVolumeOf('1.5'), 1); }); + +void test('samples long voice level sequences without changing peak bars', () => { + const levels = Array(4800).fill(0.1); + levels[2000] = 0.8; + const bars = barsOf(levels); + + assert.equal(bars.length, voiceBars); + assert.equal(bars[20], 80); + assert.equal(bars[19], 10); + assert.equal(bars[21], 10); +}); + +void test('keeps clamping and NaN behavior for voice level bars', () => { + assert.deepEqual(barsOf([-0.5], 1), [0]); + assert.equal(Number.isNaN(barsOf([Number.NaN], 1)[0]), true); + + const sparse = Array(2); + sparse[1] = 0.5; + assert.equal(Number.isNaN(barsOf(sparse, 1)[0]), true); +});