74 lines
		
	
	
	
		
			1.7 KiB
			
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			74 lines
		
	
	
	
		
			1.7 KiB
			
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2023 Signal Messenger, LLC
							 | 
						||
| 
								 | 
							
								// SPDX-License-Identifier: AGPL-3.0-only
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import { noop } from 'lodash';
							 | 
						||
| 
								 | 
							
								import { useEffect, useState } from 'react';
							 | 
						||
| 
								 | 
							
								import { computePeaks } from '../components/VoiceNotesPlaybackContext';
							 | 
						||
| 
								 | 
							
								import * as log from '../logging/log';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type WaveformData = {
							 | 
						||
| 
								 | 
							
								  peaks: ReadonlyArray<number>;
							 | 
						||
| 
								 | 
							
								  duration: number;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function useComputePeaks({
							 | 
						||
| 
								 | 
							
								  audioUrl,
							 | 
						||
| 
								 | 
							
								  activeDuration,
							 | 
						||
| 
								 | 
							
								  barCount,
							 | 
						||
| 
								 | 
							
								  onCorrupted,
							 | 
						||
| 
								 | 
							
								}: {
							 | 
						||
| 
								 | 
							
								  audioUrl: string | undefined;
							 | 
						||
| 
								 | 
							
								  activeDuration: number | undefined;
							 | 
						||
| 
								 | 
							
								  barCount: number;
							 | 
						||
| 
								 | 
							
								  onCorrupted: () => void;
							 | 
						||
| 
								 | 
							
								}): { peaks: ReadonlyArray<number>; hasPeaks: boolean; duration: number } {
							 | 
						||
| 
								 | 
							
								  const [waveformData, setWaveformData] = useState<WaveformData | undefined>(
							 | 
						||
| 
								 | 
							
								    undefined
							 | 
						||
| 
								 | 
							
								  );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // This effect loads audio file and computes its RMS peak for displaying the
							 | 
						||
| 
								 | 
							
								  // waveform.
							 | 
						||
| 
								 | 
							
								  useEffect(() => {
							 | 
						||
| 
								 | 
							
								    if (!audioUrl) {
							 | 
						||
| 
								 | 
							
								      return noop;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    log.info('MessageAudio: loading audio and computing waveform');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let canceled = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    void (async () => {
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        const { peaks: newPeaks, duration: newDuration } = await computePeaks(
							 | 
						||
| 
								 | 
							
								          audioUrl,
							 | 
						||
| 
								 | 
							
								          barCount
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								        if (canceled) {
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        setWaveformData({
							 | 
						||
| 
								 | 
							
								          peaks: newPeaks,
							 | 
						||
| 
								 | 
							
								          duration: Math.max(newDuration, 1e-23),
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								      } catch (err) {
							 | 
						||
| 
								 | 
							
								        log.error(
							 | 
						||
| 
								 | 
							
								          'MessageAudio: computePeaks error, marking as corrupted',
							 | 
						||
| 
								 | 
							
								          err
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        onCorrupted();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    })();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return () => {
							 | 
						||
| 
								 | 
							
								      canceled = true;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }, [audioUrl, barCount, onCorrupted]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    duration: waveformData?.duration ?? activeDuration ?? 1e-23,
							 | 
						||
| 
								 | 
							
								    hasPeaks: waveformData !== undefined,
							 | 
						||
| 
								 | 
							
								    peaks: waveformData?.peaks ?? new Array(barCount).fill(0),
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 |