jquery - Unable to access the microphone using Safari and Chrome browsers on iPhone - Stack Overflow

admin2025-04-15  1

I am not able to access the microphone of browsers like Safari and Chrome on iPhone using jQuery code.

When I open the page and click the start button, I am stuck on listening mode. Instead of that, I want to process the audio for Ajax. The same code works fine on Windows.

jQuery(document).ready(function() {
    var baseURL = window.location.origin + "/";

    let mediaRecorder;
    let audio;
    let typingInterval; // Declare this variable outside so it is accessible globally

    const startButton = $("#startRecording");
    const stopButton = $("#stopButton");
    const resetButton = $("#resetButton");
    const bottomroundbtns = $(".bottomroundbtns, .bottomroundbtns1");

    startButton.on('click', function() {
        $(".index1").hide();
        $(".index2").show();
        $("#index3").hide();
        $("#index4").hide();
        startRecording();
    });

    resetButton.on('click', function() {
        startRecording();
        $(".index2").show();
        $('.listn').html('Listening...');
        $(".index3").hide();
        $(".index4").hide();
    });

    // start recording without silence detection
    async function startRecording() {
        try {
            const stream = await navigator.mediaDevices.getUserMedia({
                audio: true
            });

            const audioContext = new(window.AudioContext || window.webkitAudioContext)();
            const microphone = audioContext.createMediaStreamSource(stream);

            const mimeType = 'audio/webm;codecs=opus';
            mediaRecorder = new MediaRecorder(stream, {
                mimeType
            });

            mediaRecorder.ondataavailable = async (event) => {
                if (event.data.size > 0) {
                    await sendAudioChunk(event.data);
                }
            };

            mediaRecorder.start();

            // Wait for 10 seconds before calling the ajax
            setTimeout(() => {
                mediaRecorder.stop();
            }, 10000);

        } catch (error) {
            console.log("Microphone error:", error);
            $('.listn').html('Microphone access denied.');
        }
    }

    async function sendAudioChunk(audioBlob) {
        const formData = new FormData();
        formData.append("audio_chunk", audioBlob);
        try {
            $('.listn').html('Processing...');
            const response = await fetch(baseURL + "process_audio", {
                method: "POST",
                body: formData
            });
            const result = await response.json();
            if (result.audio_content && result.transcription) {
                playAudioFromBase64(result.audio_content, result.chat_response);
            } else {
                console.log("Unable to generate response.");
            }
        } catch (error) {
            console.log("Error sending audio chunk to server:", error);
        }
    }

    function playAudioFromBase64(base64Audio, transcription) {
        $(".index2").hide();
        $(".index3").show();

        audio = new Audio("data:audio/mp3;base64," + base64Audio);
        audio.play();

        // Visualizer initialization before audio starts
        startVisualizer();

        audio.addEventListener('loadedmetadata', () => {
            const duration = audio.duration;
            typewriterEffect(transcription, duration);
        });

        audio.addEventListener('ended', () => {
            $(".index3").show();
            $("#index3").hide();
            $(".index4").show();
        });

    }

    function typewriterEffect(text, audioDuration) {
        $('#index3').show();
        $('#txtResponse').text('');

        const words = text.split(' ');
        let index = 0;
        const numWords = words.length;

        // Calculate interval based on the audio duration
        let interval = audioDuration / numWords;

        // Set a moderate minimum interval (500ms between words)
        interval = Math.max(interval, 500); // Minimum interval of 500ms per word

        // Set a moderately fast typing speed with a minimum interval of 250ms
        const minInterval = 250; // Moderate typing effect with a minimum interval of 250ms
        typingInterval = setInterval(() => {
            if (index < numWords) {
                $('#txtResponse').append(words[index] + " ");
                index++;
            } else {
                clearInterval(typingInterval); // Stop typing effect once all words are typed
            }
        }, Math.max(minInterval, interval));
    }

    // Stop button to stop audio recording
    stopButton.on("click", () => {
        stopAudioAndReset();
    });

    // Stop and reset the audio recording process
    function stopAudioAndReset() {
        $('#index3').hide();
        $('.index4').show();
        if (audio) {
            audio.pause();
            audio.currentTime = 0;
        }
        if (typingInterval) {
            clearInterval(typingInterval); // Clear the typing effect interval here
        }
        // $('#txtResponse').text(''); // Optionally, clear the text on reset
    }

    // Handle click on individual <p> tags inside .bottomroundbtns
    bottomroundbtns.on("click", "p", async function() {
        $(".index1").hide();
        $(".index2").show();
        $(".listn").html('Processing...');

        const text = $(this).text();

        $.ajax({
            url: baseURL + "process_audio",
            type: "POST",
            dataType: "json",
            data: {
                transcription_text: text
            },
            success: function(result) {
                if (result.audio_content && result.transcription) {
                    playAudioFromBase64(result.audio_content, result.chat_response);
                    $('#index2').hide();
                    $('#index3').show();
                } else {
                    console.log("Unable to generate response.");
                }
            },
            error: function(err) {
                console.log("AJAX error:", err);
            }
        });
    });

    // visualizer function
    navigator.mediaDevices.getUserMedia({
            audio: true
        })
        .then(function(stream) {
            const audioContext = new(window.AudioContext || window.webkitAudioContext)();
            const analyser = audioContext.createAnalyser();
            const microphone = audioContext.createMediaStreamSource(stream);
            microphone.connect(analyser);

            analyser.fftSize = 512;
            const bufferLength = analyser.frequencyBinCount;
            const dataArray = new Uint8Array(bufferLength);

            const canvas = document.getElementById('visualizer');
            const canvasCtx = canvas.getContext('2d');
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;

            const globeRadius = Math.min(canvas.width, canvas.height) / 3.5; // Increase radius for full-screen
            const points = []; // Points on the globe
            const numPoints = 800; // Increase number of points for density

            // Create 3D points on the globe
            for (let i = 0; i < numPoints; i++) {
                const theta = Math.random() * Math.PI * 2; // Random angle around the z-axis
                const phi = Math.acos((Math.random() * 2) - 1); // Random angle from z-axis
                const x = globeRadius * Math.sin(phi) * Math.cos(theta);
                const y = globeRadius * Math.sin(phi) * Math.sin(theta);
                const z = globeRadius * Math.cos(phi);
                points.push({
                    x,
                    y,
                    z
                });
            }

            function animateVisualizer() {
                requestAnimationFrame(animateVisualizer);

                // Get audio data
                analyser.getByteFrequencyData(dataArray);

                // Clear the canvas
                canvasCtx.clearRect(0, 0, canvas.width, canvas.height);

                // Rotate points and draw the globe
                for (let i = 0; i < points.length; i++) {
                    const point = points[i];

                    // Rotate around the Y-axis
                    const angleY = 0.005; // Slower rotation for smoother effect
                    const sinY = Math.sin(angleY);
                    const cosY = Math.cos(angleY);
                    const xRot = point.x * cosY - point.z * sinY;
                    const zRot = point.x * sinY + point.z * cosY;
                    point.x = xRot;
                    point.z = zRot;

                    // Project 3D point to 2D space
                    const scale = globeRadius / (globeRadius + point.z);
                    const x2d = point.x * scale + canvas.width / 2;
                    const y2d = point.y * scale + canvas.height / 2;

                    // Use audio data to modulate the brightness and size of the points
                    const barHeight = dataArray[i % bufferLength];
                    const size = Math.max(2, barHeight / 40); // Size changes based on amplitude
                    const alpha = Math.max(0.2, barHeight / 200); // Modulate opacity for a pulse effect

                    // Add glow effect for brightness
                    canvasCtx.shadowBlur = barHeight > 20 ? size * 4 : size * 2; // Brighten on sound
                    canvasCtx.shadowColor = `rgba(255, 50, 50, ${alpha})`; // Bright red glow color

                    // Draw the point
                    canvasCtx.beginPath();
                    canvasCtx.arc(x2d, y2d, size, 0, Math.PI * 2);
                    canvasCtx.fillStyle = `rgba(255, 50, 50, ${alpha})`; // Bright red color
                    canvasCtx.fill();
                }
            }

            animateVisualizer();
        })
        .catch(function(err) {
            console.log('Error accessing microphone: ', err);
        });

    /**
     * start the visualizer once the audio starts playing
     */
    function startVisualizer() {
        if (!audio) return;

        const audioContext = new(window.AudioContext || window.webkitAudioContext)();
        const analyser = audioContext.createAnalyser();
        const source = audioContext.createMediaElementSource(audio);
        source.connect(analyser);
        analyser.connect(audioContext.destination);
        analyser.fftSize = 512;
        const bufferLength = analyser.frequencyBinCount;
        const dataArray = new Uint8Array(bufferLength);

        const canvas = document.getElementById('visualizer');
        const canvasCtx = canvas.getContext('2d');
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;

        const globeRadius = Math.min(canvas.width, canvas.height) / 3.5;
        const points = [];
        const numPoints = 800;

        // Create 3D points
        for (let i = 0; i < numPoints; i++) {
            const theta = Math.random() * Math.PI * 2;
            const phi = Math.acos((Math.random() * 2) - 1);
            const x = globeRadius * Math.sin(phi) * Math.cos(theta);
            const y = globeRadius * Math.sin(phi) * Math.sin(theta);
            const z = globeRadius * Math.cos(phi);
            points.push({
                x,
                y,
                z
            });
        }

        // Enhanced animation function with real-time frequency analysis
        function animateVisualizer() {
            requestAnimationFrame(animateVisualizer);

            analyser.getByteFrequencyData(dataArray); // Get real-time frequency data

            canvasCtx.clearRect(0, 0, canvas.width, canvas.height); // Clear previous frame

            // Rotate and project points with dynamic effects based on frequency data
            for (let i = 0; i < points.length; i++) {
                const point = points[i];

                const angleY = 0.005;
                const sinY = Math.sin(angleY);
                const cosY = Math.cos(angleY);
                const xRot = point.x * cosY - point.z * sinY;
                const zRot = point.x * sinY + point.z * cosY;
                point.x = xRot;
                point.z = zRot;

                const scale = globeRadius / (globeRadius + point.z);
                const x2d = point.x * scale + canvas.width / 2;
                const y2d = point.y * scale + canvas.height / 2;

                const barHeight = dataArray[i % bufferLength];
                const size = Math.max(2, barHeight / 40);
                const alpha = Math.max(0.2, barHeight / 200);

                canvasCtx.shadowBlur = barHeight > 20 ? size * 4 : size * 2;
                canvasCtx.shadowColor = `rgba(255, 50, 50, ${alpha})`;

                canvasCtx.beginPath();
                canvasCtx.arc(x2d, y2d, size, 0, Math.PI * 2);
                canvasCtx.fillStyle = `rgba(255, 50, 50, ${alpha})`;
                canvasCtx.fill();
            }
        }

        animateVisualizer(); // Start the animation loop
    }
});
转载请注明原文地址:http://www.anycun.com/QandA/1744721579a86704.html