export function useAudio()

in frontend/src/hooks/useAudio.ts [6:110]


export function useAudio() {
  const wavRecorder = useRef<WavRecorder | null>(null);
  const wavPlayer = useRef<WavStreamPlayer | null>(null);
  const audioChunks = useRef<Int16Array[]>([]);
  const trackId = useRef<string | null>(null);
  const [frequencies, setFrequencies] = useState<number[]>([]);

  const [audioPlayerIsReady, setAudioPlayerIsReady] = useState(false);
  const [audioRecorderIsReady, setAudioRecorderIsReady] = useState(false);
  const [playbackFrequencies, setPlaybackFrequencies] = useState<number[]>([]);

  const stoppedManually = useRef(false);

  useEffect(() => {
    async function init() {
      wavRecorder.current = new WavRecorder({ sampleRate: 24000 });
      await wavRecorder.current.begin();
      setAudioRecorderIsReady(true);
      wavPlayer.current = new WavStreamPlayer({ sampleRate: 24000 });
      await wavPlayer.current.connect();
      setAudioPlayerIsReady(true);
    }
    init();
  }, []);

  const getFrequencies = useCallback(async () => {
    if (wavPlayer.current) {
      const newFrequencies = wavPlayer.current.getFrequencies("voice").values;
      const normalizedFrequencies = normalizeArray(newFrequencies, 5);
      setPlaybackFrequencies(normalizedFrequencies);

      const status = await wavPlayer.current?.getTrackSampleOffset();
      if (status) {
        window.requestAnimationFrame(getFrequencies);
      } else {
        setPlaybackFrequencies([]);
      }
    }
  }, []);

  const playAudio = useCallback(
    (audio: Int16Array<ArrayBuffer>) => {
      if (wavPlayer.current) {
        wavPlayer.current.add16BitPCM(audio, trackId.current ?? undefined);
        window.requestAnimationFrame(getFrequencies);
      }
    },
    [getFrequencies]
  );

  async function startRecording() {
    await stopPlaying();
    stoppedManually.current = false;
    trackId.current = crypto.randomUUID();
    await wavRecorder.current?.clear();
    audioChunks.current = [];
    await wavRecorder.current?.record((data) => {
      audioChunks.current.push(data.mono);
      const updatedFrequencies = wavRecorder.current?.getFrequencies(
        "voice"
      ) || {
        values: new Float32Array([0]),
      };
      setFrequencies(normalizeArray(updatedFrequencies.values, 30));
    });
  }

  async function stopPlaying() {
    stoppedManually.current = true;
    await wavPlayer.current?.interrupt();
    setPlaybackFrequencies(Array.from({ length: 30 }, () => 0));
  }

  async function stopRecording() {
    await wavRecorder.current?.pause();
    const dataArrays = audioChunks.current.map((chunk) => {
      return new Int16Array(chunk);
    });

    const totalLength = dataArrays.reduce(
      (acc, chunk) => acc + chunk.length,
      0
    );
    const mergedAudio = new Int16Array(totalLength);
    let offset = 0;
    dataArrays.forEach((chunk) => {
      for (let i = 0; i < chunk.length; i++) {
        mergedAudio[offset + i] = chunk[i];
      }
      offset += chunk.length;
    });

    return mergedAudio;
  }

  return {
    isReady: audioPlayerIsReady && audioRecorderIsReady,
    playAudio,
    startRecording,
    stopRecording,
    stopPlaying,
    frequencies,
    playbackFrequencies,
  };
}