Search code examples
javascripthtmlaudioblazorweb-audio-api

Blazor play generated sound without file


In a Blazor project I generate sound/noise signals as @DanW 's pink noise example, that generates a double[] with values between -1.0 - 1.0. Is it possible to play this array directly as audio in the browser? All I have found about sound/audio and browsers so far has been playing audio from an existing file.

EDIT: I am doing some filtering using some native dll's in C# and am more comfortable in c# than in javascript, hence trying to do most work in C# and not javascript.


Solution

  • So I managed with the WebAudio API to figure out how to:

    Javascript:

    // for cross browser compatibility
    const AudioContext = window.AudioContext || window.webkitAudioContext;
    audioCtx = {};
    
    function initAudio() {
        // creation of audio context cannot not be done on loading file, must be done afterwards
        audioCtx = new AudioContext();
        return audioCtx.sampleRate;
    }
    
    // floats is a 1-D array with channel data after each other:
    // ch1 = floats.slice(0,nSamples)
    // ch2 = floats.slice(nSamples,nSamples*2)
    function playNoise(floats, nChannels) {
        const bufferSize = floats.length / nChannels;
        let arrayBuffer = audioCtx.createBuffer(nChannels, bufferSize, audioCtx.sampleRate);
    
        for (var i = 0; i < nChannels; i++) {
            let f32arr = new Float32Array(floats.slice(i * bufferSize, (i + 1) * bufferSize));
            arrayBuffer.copyToChannel(f32arr, i, 0);
        }
    
        // Creating AudioBufferSourceNode
        let noise = audioCtx.createBufferSource();
        noise.buffer = arrayBuffer;
        noise.connect(audioCtx.destination);
        noise.start();
        return true;
    }
    

    Blazor page (I use Blazorise (Button)):

    @page "/Tests"
    @inject IJSRuntime js
    <h3>Test</h3>
    <Button Clicked="@(async () => await TestJS())" Color="Color.Primary">Test JS</Button>
    
    @code {
        double fs;
        protected override async Task OnInitializedAsync()
        {
            fs = await js.InvokeAsync<double>("initAudio");
            await base.OnInitializedAsync();
        }
    
        private async Task TestJS()
        {
            var nChannels = 2;
            var nSecs = 5;
            var nSampels = (int)fs * nSecs * nChannels;
            var floats = new float[nSampels];
            var freq = 440;
            for (int i = 0; i < nSampels / nChannels; i++)
            {
                floats[i] = (float)Math.Sin(i * freq * 2 * Math.PI / fs);
                floats[i + nSampels / 2] = (float)Math.Sin(i * freq * 2 * 2 * Math.PI / fs);
            }
            var ok = await js.InvokeAsync<bool>("playNoise", floats, nChannels);
        }
    }
    

    The button plays a 440 Hz tone in the left channel (chanel 1) and a 880 Hz tone in the right channel (channel 2).

    EDIT: Samplerate does not have to be the same as in the AudioContext. Check here for specs.