Stream audio content directly in your browser

I am currently working on a project involving an IP camera that streams audio data in a unique format. Unlike common formats like MP3 or WAV, this audio is transmitted in ADPCM chunks of 544 bytes each, with a 32-byte header that needs to be removed for playback. To tackle this challenge, I am developing browser-side Javascript code with the goal of creating a browser extension that can consume this stream, strip the header, convert it to PCM, and play it seamlessly.

Below is a snippet of my code that has shown promising results in Firefox:

    <script>
        let form = document.forms.audioStreamSettings;
        let inputAudioStreamURL = form.elements.audioStreamURL;
        let inputAudioChunksLimit = form.elements.audioChunksLimit;
        let btnStartStream = form.elements.startStream;
        //Initiate audio playback upon clicking the "Play audio stream" button:
        btnStartStream.addEventListener('click', () => {
            let audioStreamURL = inputAudioStreamURL.value;
            let audioChunksLimit = inputAudioChunksLimit.value;
            (async () => {
                let response = await fetch(audioStreamURL);
                const reader = response.body.getReader();
                
                const audioCtx = new (window.AudioContext || window.webkitAudioContext)();

                let receivedLength = 0; 
                let pcmStream = new Float32Array();
                let audioDataChunks = new Uint8Array(reader.result); 
                for (let step = 0; step < audioChunksLimit; step++) {
                    const {done, value} = await reader.read();
                    if (done) {
                    break;
                    }
                    let audioChunk = new Uint8Array(value);

                    adpcmRawChunk = audioChunk.slice(32,544);
                    
                    var pcmChunk = decodeAdpcm(adpcmRawChunk);

                    pcmStream = Float32Array.from([...pcmStream,...pcmChunk]);

                    receivedLength += audioChunk.length;
                }

                const audioBuffer = audioCtx.createBuffer(
                    1,
                    pcmStream.length,
                    8000
                );
                    
                for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
                    const nowBuffering = audioBuffer.getChannelData(channel);
                    for (let i = 0; i < audioBuffer.length; i++) {
                        nowBuffering[i] = pcmStream[i];
                    }
                }

                const source = audioCtx.createBufferSource();

                source.buffer = audioBuffer;

                source.connect(audioCtx.destination);

                source.start();
            })()
        })

    </script>

While this code successfully captures and plays back a set number of audio chunks, my ultimate goal is to achieve real-time and continuous audio streaming, as well as implement a stop button for playback. I would appreciate any guidance or suggestions on how to enhance this code further to meet these objectives.

Answer №1

If you want to streamline the process for others:

async function playAudioPCMStream(url: string){
    const res = await fetch(url)
    const reader = res.body!.getReader();
    const audioCtx = new AudioContext() // Consider creating it upon user interaction on iOS
    await streamRawAudio(reader, audioCtx);
}

async function streamRawAudio(
  reader: ReadableStreamDefaultReader<Uint8Array>,
  audioCtx: AudioContext,
) {
  while (true) {
    const { done, value } = await reader.read();

    if (done) {
      break;
    }

    await playAudioData(value, audioCtx);
  }
}

async function playAudioData(
  audioData: Uint8Array,
  audioContext: AudioContext,
) {
  // Refer to https://stackoverflow.com/questions/60921018/web-audio-api-efficiently-play-a-pcm-stream
  const pcmAudio: Float32Array = new Float32Array(audioData.buffer);
  const audioBuffer = audioContext.createBuffer(1, pcmAudio.length, 24000);
  audioBuffer.copyToChannel(pcmAudio, 0);
  const source = audioContext.createBufferSource();
  source.buffer = audioBuffer;
  source.connect(audioContext.destination);
  source.start();
  await new Promise((resolve) => (source.onended = resolve));
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Retrieving text from a draggable div using jQuery

I have a draggable div that I can move over another element with the class .outerDiv which contains text content. Is there a way for me to retrieve the text from .outerDiv that overlaps with the draggable div? $(".outerDiv .isStore").draggable({ contain ...

Merging audio files using Node Stream

Currently facing an issue with joining 2 .wav files together, as my output file only contains the first item in the array: const writeStream = fs.createWriteStream('server/playback.wav'); const inputFiles = [ `${path.resolve(__dirname, 'r ...

Display the chosen alternative in a Bootstrap dropdown menu

I am currently facing an issue with the dropdown list in my bootstrap menu. <li class="dropdown"> <a aria-expanded="false" aria-haspopup="true" role="button" data-toggle="dropdown" class="dropdown-toggle" href="#">Chose option<span class="c ...

Ways to retrieve the value of the chosen radio button using Prototype or JQuery

Looking for a way to retrieve the value of the selected radio button using either JQuery or Prototype? Check out this sample form: <label for="deliveryChoice">Courier&nbsp;<b>&pound;3.00</b></label> <input type="radio" ...

sending encoded strings from django to javascript

My data contains a collection of unicode strings that I need to transfer from my django view to a template for use in a JavaScript scriptlet that handles communication with the web interface. The issue I'm facing is that Python adds a u prefix to the ...

In order to resolve this issue, I must eliminate any duplicate objects and then calculate the total sum using JavaScript

I have successfully removed the duplicates so far, but now I am stuck on how to sum the Total_Quantity. Is there a way to achieve this within the reduced method itself? Any help would be appreciated. Thank you. const test = [ { Item_Nam ...

What are the steps to enable a web app on a user's home screen?

Hey there! I'm looking to add a small popup with a button at the end of my website to prompt users to add it to their phone's home screen. I followed a tutorial that helped me achieve this on Android, but unfortunately, it only works with https a ...

Looking for a way to access session values in JavaScript? If your JavaScript code is located in the code behind of an ASP.NET application, you may be wondering how to retrieve and set variable values

Struggling to retrieve the session value in my asp.net code behind using JavaScript. The syntax seems incorrect and I cannot figure out how to access the session value. I attempted to invoke JavaScript on page load, which worked without issues. However, w ...

Discover the lowest value within an array of objects while applying specific conditions

let event = [ { "vendorBidId": 58, "participantName": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b5d7c7da9bd2d0d0f5c1d0c6c19bdcdb">[email protected]</a>", ...

"Maintaining the jQuery carousel slider above the lightbox image for a seamless user experience

Being a newcomer to jQuery, I am currently relying solely on plugins. I recently installed a carousel slider that allows manual sliding to view images accompanied by text information underneath. By clicking on "more" at the bottom of the image/text box, an ...

react-i18next - The function call does not match any overload when the specified type is `string`

I am currently utilizing react-i18next in conjunction with React and TypeScript. Interestingly, when I attempt to load a property using a string literal and type inference, everything works seamlessly. However, once I specify the type as string, an error i ...

Can we utilize an array as parameters in Angular's $http query function?

In my Angular 1 application, users can select multiple items by checking checkboxes. When a specific button is clicked, the selected IDs are aggregated like so: angular.forEach($scope.orders, function (order, id) { if (order.export) { ids.push ...

Is there a way to execute server-side functions with HtmlService?

I am currently learning programming and I'm experimenting with setting up buttons using jQuery in Google Apps Script. I have a spreadsheet with a menu that opens a dialog box created with HtmlService. Inside the dialog box, there are two buttons - o ...

How can I execute a MySQL query by clicking on a link using the onclick event?

I am currently facing an issue with a PHP script. My goal is to execute a MySQL query in my PHP code when I click on a specific link. Here is the code I have: <?php function hello(){ $browser=$_SERVER['HTTP_USER_AGENT']; $url="http ...

Manipulating Arrays with Conditions in JavaScript

Here is an array I need to work with: 0: {productid: "001", containersize: "20", ContCount: 10} 1: {productid: "002", containersize: "20", ContCount: 9} 2: {productid: "001", containersize: "40", ContCount: 4} 3: {productid: "001", containersize: "20", C ...

Is it possible to create a dynamic zig-zag design with CSS that

I am looking to design a dynamic snake/zigzag layout that consists of square images and circles, starting from the center of the container and descending in a winding fashion. The number of elements is not fixed and is generated based on data received fro ...

Dominant Editing through ASP.Net Roles

Looking for guidance on how to effectively use knockout with asp.net membership roles in MVC 4. My goal is to incorporate an editable grid on the page based on whether the user is an administrator or a 'registered user'. I want to ensure that use ...

Error encountered while creating SvelteKit: The module 'intl-messageformat' that was requested is in CommonJS format

Encountering an error during production build. Here's the output of npm run build executed on Node v16.20.1 npm run build > <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1c7a6c6a317a757278796e5c2c322c322d">[email&# ...

What is the best way to display data from an array using ng-container in Angular 2?

My goal is to display data from an array in an HTML table using the Section model provided below: export class Section { public id :number; public name: string; constructor(id: number, theName: string) { this.id = id; this.name ...

Event handler for "copy" on the iPad

Is it possible to bind an event handler to the copy event on iPad or iPhone devices? ...