Web-sys audio quality improvement

Hi,

I'm trying to build media streaming platform. I'm currently experiencing low quality audio. Do you know what should I do to improve quality ?

here is a video

my code:

use leptos::{
    IntoView, ev,
    html::{ElementChild, button},
    logging::log,
    prelude::{OnAttribute, Read, Show, ShowProps, ToChildren},
    server::LocalResource,
    task::spawn_local,
};
use wasm_bindgen_futures::JsFuture;
use web_sys::{
    HtmlAudioElement, MediaStream, MediaStreamConstraints,
    wasm_bindgen::{JsCast, JsValue},
    window,
};

pub fn app() -> impl IntoView {
    let audio_stream = LocalResource::new(|| media());
    let props = ShowProps::builder()
        .when(move || audio_stream.read().is_some())
        .children(ToChildren::to_children(move || {
            let audio_element = HtmlAudioElement::new().unwrap();
            let audio_stream = audio_stream.read();
            let audio_stream = audio_stream.as_deref();
            audio_element.set_src_object(audio_stream);
            button()
                .on(ev::click, move |_| match audio_element.play() {
                    Ok(audio_element_play_promise) => {
                        log!("{}", "Play will");
                        spawn_local(async move {
                            JsFuture::from(audio_element_play_promise).await.ok();
                        });
                        log!("{}", "Play must");
                    }
                    Err(err_val) => log!("{:#?}", err_val),
                })
                .child("Happy Button")
                .into_view()
        }))
        .fallback(|| button().child("Sad Button"))
        .build();
    Show(props)
}

async fn media() -> MediaStream {
    let media_devices = window().unwrap().navigator().media_devices().unwrap();
    let constraints = MediaStreamConstraints::new();
    constraints.set_audio(&JsValue::TRUE);
    let media_stream_promise = media_devices
        .get_user_media_with_constraints(&constraints)
        .unwrap();
    let media_stream = JsFuture::from(media_stream_promise)
        .await
        .unwrap()
        .dyn_into::<MediaStream>()
        .unwrap();
    let audio_tracks = media_stream.get_audio_tracks();
    MediaStream::new_with_tracks(&audio_tracks).unwrap()
}

This problem also happens in pure JavaScript too.
I got code from: audio - Access microphone from a browser - Javascript - Stack Overflow

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>
</head>
<body>
    <audio id="audioCapture"></audio>

    <button onclick=audioCapture.Init()>
        start microphone
    </button>

    <p style="font-size: 42pt;">
        This is a blank page for live captions like CNN video.
    </p>


<script type="text/javascript">

var audioCapture = (function ()
{
    var init = function ()
    {
        initCapture();
    };

    function initCapture()
    {
        var constraints = { audio: true, video: false };

        navigator.mediaDevices.getUserMedia(constraints).then(function (stream)
        {
            var audio = document.getElementById('audioCapture');
            audio.srcObject = stream;
            audio.play();
        }).catch(function (err)
        {
            console.log(err);
        });
    }

    return {
        Init: init
    }
})();

</script>


</body>
</html>

Alright solved.
tldr: need to disable echo cancellation.
details: How to improve audio performance · leptos-rs/leptos · Discussion #3809 · GitHub

improved code:

use leptos::{
    IntoView, ev,
    html::{ElementChild, button},
    logging::log,
    prelude::{OnAttribute, Read, Show, ShowProps, ToChildren},
    server::LocalResource,
    task::spawn_local,
};
use wasm_bindgen_futures::JsFuture;
use web_sys::{
    HtmlAudioElement, MediaStream, MediaStreamConstraints, MediaStreamTrack, MediaTrackConstraints,
    wasm_bindgen::{JsCast, JsValue},
    window,
};

pub fn app() -> impl IntoView {
    let audio_stream = LocalResource::new(|| media());
    let props = ShowProps::builder()
        .when(move || audio_stream.read().is_some())
        .children(ToChildren::to_children(move || {
            let audio_element = HtmlAudioElement::new().unwrap();
            let audio_stream = audio_stream.read();
            let audio_stream = audio_stream.as_deref();
            audio_element.set_src_object(audio_stream);
            button()
                .on(ev::click, move |_| match audio_element.play() {
                    Ok(audio_element_play_promise) => {
                        log!("{}", "Play will");
                        spawn_local(async move {
                            JsFuture::from(audio_element_play_promise).await.ok();
                        });
                        log!("{}", "Play must");
                    }
                    Err(err_val) => log!("{:#?}", err_val),
                })
                .child("Happy Button")
                .into_view()
        }))
        .fallback(|| button().child("Sad Button"))
        .build();
    Show(props)
}

async fn media() -> MediaStream {
    let media_devices = window().unwrap().navigator().media_devices().unwrap();
    let media_stream_constraints = MediaStreamConstraints::new();
    let media_track_constraints = MediaTrackConstraints::new();

    media_stream_constraints.set_audio(&JsValue::TRUE);

    media_track_constraints.set_echo_cancellation(&JsValue::FALSE);
    media_track_constraints.set_noise_suppression(&JsValue::FALSE);
    media_track_constraints.set_auto_gain_control(&JsValue::FALSE);

    let media_stream_promise = media_devices
        .get_user_media_with_constraints(&media_stream_constraints)
        .unwrap();
    let media_stream = JsFuture::from(media_stream_promise)
        .await
        .unwrap()
        .dyn_into::<MediaStream>()
        .unwrap();
    let audio_tracks = media_stream.get_audio_tracks();
    let audio_tracks = audio_tracks
        .iter()
        .map(|audio_track| audio_track.dyn_into::<MediaStreamTrack>().unwrap())
        .collect::<Vec<_>>();
    log!("{:#?}, len = {}", audio_tracks, audio_tracks.len());
    let audio_track = audio_tracks.first().unwrap();
    let media_track_apply_constraints_promise = audio_track
        .apply_constraints_with_constraints(&media_track_constraints)
        .unwrap();
    JsFuture::from(media_track_apply_constraints_promise)
        .await
        .unwrap();
        
    let media_stream = MediaStream::new().unwrap();
    log!("{:#?}", audio_track.get_constraints());
    media_stream.add_track(audio_track);
    media_stream
}
2 Likes