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

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.