Blazor play generated sound without file

Viewed 442

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.

3 Answers

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.

Blazor doesn't come pre-packaged with sound editing capabilities, but there is now JS isolation, which means that components can load required JS modules.

My advice is to do all your audio stuff directly in JavaScript using the WebAudio API etc. in a collection of custom-made components.

You could use C# to generate waveforms and so on, but I'm not sure there's any advantage in doing so, unless you want to use an existing C# library.

I've had no problem generating files using Java (Spring Framework) and downloading the raw PCM to Javascript variables in the html via Thymeleaf and playing them via the Web Audio API, for example, using an AudioBuffer object. Is there some aspect of Blazor that prevents or inhibits the use of Web Audio objects? IDK how HTML works when there are script tags designating different languages, if there is a way to communicate between them.

Related