2
\$\begingroup\$

My goal was to make playing sounds in my React game easy and efficient. Using <audio> tags wasn't an option, because on mobile it creates huge delays between the action and the sound. I'm just starting with the Web Audio API, so please forgive me if I did something silly. It works though.

I decided to make it a React hook called useAudio():

import { useMemo, useCallback } from "react";

const useAudio = (paths: Map<unknown, string>) => {
    const audioContext = useMemo(
        () => new AudioContext(),
        []
    );

    const arrayBuffers = useMemo(
        () => new Map([ ...paths ].map(
            ([ key, path ]) => [
                key,
                fetch(path).then(response => response.arrayBuffer())
            ]
        )),
        [paths]
    );

    const decodedTracks = useMemo(
        () => new Map([ ...arrayBuffers ].map(
            ([ key, arrayBufferPromise ]) => [
                key,
                arrayBufferPromise.then(
                    arrayBuffer => audioContext.decodeAudioData(arrayBuffer)
                )
            ]
        )),
        [arrayBuffers, audioContext]
    );

    const play = useCallback(
        async (key: unknown) => {
            if (!decodedTracks.has(key)) {
                throw new Error("Invalid track key");
            }

            const decodedTrack = await decodedTracks.get(key)!;
            const bufferSource = audioContext.createBufferSource();

            bufferSource.buffer = decodedTrack;
            bufferSource.connect(audioContext.destination);

            bufferSource.start();
        },
        [audioContext, decodedTracks]
    );

    return [ play ] as const;
};

export default useAudio;

And here's how you'd use it in your app:

const audioFiles = new Map([
    ["cat", "./audio/Meow.mp3"],
    [true, "https://pikachu.test/pikapika.wav"], // You can use any key you wish!
    [777, "./lucky.aac"],
]);

const Harmony = () => {
    const [ playAudio ] = useAudio(audioFiles);

    return (
        <nav>
            <button
                type="button"
                onClick={() => playAudio("cat")}
            >
                Meow!
            </button>
            <button
                type="button"
                onClick={() => playAudio(true)}
            >
                Pika pika
            </button>
            <button
                type="button"
                onClick={() => playAudio(123)}
            >
                Gonna throw an error at ya!
            </button>
        </nav>
    );
};

I wanted to allow using any format of a key, hence using Map of unknown keys.

The main reason I'm asking for your reviews is to understand if I'm using Web Audio API correctly and if I could further optimize it, or perhaps I over-optimized something at the cost of clarity? I tried to memoize and reuse as much as I knew was possible.

\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.