b-m-f
b-m-f

Reputation: 1328

Convert Canvas to audio in Browser for datamoshing

I want to do some datamoshing in the browser and would like to load the ImageData from a Canvas into AudioData, so I can use some audio libraries (like tuna) to add effects on the Data.

This is what I have tried so far

const canvasToBlob = (canvas) => {
  let dataUrl = canvas.toDataURL('image/png');
  return dataURItoBlob(dataUrl);
};

const dataURItoBlob = (dataURI) => {
  // convert base64 to raw binary data held in a string
  // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
  let byteString = atob(dataURI.split(',')[1]);

  // separate out the mime component
  let mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
  console.log(mime);

  // write the bytes of the string to an ArrayBuffer
  let ab = new ArrayBuffer(byteString.length);
  let ia = new Uint8Array(ab);
  for (let i = 0; i < byteString.length; i++) {
    ia[i] = byteString.charCodeAt(i);
  };

  // write the ArrayBuffer to a blob, and you're done
  let blob = new Blob([ab], {type: 'audio/wav'});
  return blob;
};

export default function audioConvert (canvas) {
  const imageBlob = canvasToBlob(canvas);
  let audioCtx = new AudioContext();
  let fileReader = new FileReader();

  fileReader.onloadend = () => {
    console.log(fileReader.result);
    audioCtx.decodeAudioData(fileReader.result).then((decodedData) => {
      console.log(decodedData);
    });
  };
  fileReader.readAsArrayBuffer(imageBlob);
};

Unfortunately I only get the error

Uncaught (in promise) DOMException: Unable to decode audio data

How could I change my code to get an AudioContext.

Or is an AudioContext even the correct way?

Some effects that I would like to add to the Image are 'Echoing', or 'Wah-Wah' as found f.e. in Audacity.

Upvotes: 0

Views: 373

Answers (1)

Blindman67
Blindman67

Reputation: 54026

Image to Canvas to Audio context soundbuffer example

No answer yet so I have just added a very quick load image and convert to soundbuffer and play.

// Stereo
var log = (data)=>console.log(data);
var audioCtx = new AudioContext(); // get audio context
var pixelPlayTime,sBuffers,ctx,can,startTime,source;  // sBuffers is the wav form array
// load image
var image = new Image;
image.setAttribute('crossOrigin', 'anonymous');
image.src = "https://upload.wikimedia.org/wikipedia/commons/5/5a/Hoverfly07.jpg";

// when loaded convert to wave
image.onload = function(){
    displayit.innerHTML = "";

    // create a canvas to read pixel data
    can = document.createElement("canvas");
    can.width = this.width;
    can.height = this.height;
    ctx = can.getContext("2d");
    ctx.drawImage(this,0,0);
    var data = ctx.getImageData(0,0,this.width,this.height).data;

    // get image size and workout sound buffer sizes
    var pixelCount = this.width * this.height;
    pixelPlayTime = pixelCount / audioCtx.sampleRate;
    sBuffers = audioCtx.createBuffer(2, pixelCount, audioCtx.sampleRate);

    // left gets red full vol and blue half
    var channel1 = sBuffers.getChannelData(0); // get channel 0
    var i = 0;
    while(i < pixelCount){
        // get red and some of green for this channel
        var sample = ((data[i*4]/128) - 1 + (data[i*4+2]/255) - 0.5) / 1.5;
        channel1[i] = sample < -1 ? -1 : sample > 1 ? 1 : sample;
        i ++
    }

    // Right gets green full vol and blue half
    var channel2 = sBuffers.getChannelData(1); // get channel 1
    i = 0;
    while(i < pixelCount){
        // get blue and some of green for this channel
        var sample = ((data[i*4 + 1]/128) - 1 + (data[i*4+2]/255) - 0.5) / 1.5;
        channel2[i] = sample < -1 ? -1 : sample > 1 ? 1 : sample;
        i ++
    }

    // sound buffers created prep to play
    source = audioCtx.createBufferSource(); // for playing the buffer
    source.buffer = sBuffers;              // point to the buffer
    source.connect(audioCtx.destination);       // connect

    // show image as canvas
    can.style.width = Math.floor(this.width / 4) + "px"
    can.style.height = Math.floor(this.height / 4) + "px"
    displayit.innerHTML = "Click image to hear it<br>";
    displayit.appendChild(can);

    // Wait for clien click
    can.addEventListener("click",()=>{
      requestAnimationFrame(startSounds);
     });

}
// start playing the sount
function startSounds(time){
    if(startTime === undefined){
      startTime = time;
      pixelPlayTime *= 1000; // convert from seconds to micro seconds
      source.start();                             // and play dat phunky fly
      requestAnimationFrame(update);
      ctx.fillStyle = "white";
    }
}

// display where the sound data is on the image.
// Basic timing use to show at what line the sound is being generated from.
function update(time){
    var pos = (time - startTime) / pixelPlayTime;
    var posY = Math.floor(pos * can.height);
    ctx.globalCompositeOperation = "lighter";
    ctx.fillRect(0,posY,can.width,4);
    ctx.globalCompositeOperation = "source-over";
    ctx.drawImage(image,0,posY-4,can.width,4,0,posY-4,can.width,4);
    if(pos < 1){
        requestAnimationFrame(update);
    }
}
<div id="displayit" style="font-family:arial"><h2>Please wait loading image</h2></div>

<div style="font-family:arial">
<b><a href="https://en.wikipedia.org/wiki/User:Fir0002"  title="en:User:Fir0002">fir0002</a> | <a  href="http://www.flagstaffotos.com.au">flagstaffotos.com.au</a></b>



Image attribution
<table>
<tbody><tr>
<td style="width:90px;"><img alt="" src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/79/CC_some_rights_reserved.svg/90px-CC_some_rights_reserved.svg.png" width="90" height="36" srcset="https://upload.wikimedia.org/wikipedia/commons/thumb/7/79/CC_some_rights_reserved.svg/135px-CC_some_rights_reserved.svg.png 1.5x, https://upload.wikimedia.org/wikipedia/commons/thumb/7/79/CC_some_rights_reserved.svg/180px-CC_some_rights_reserved.svg.png 2x" data-file-width="744" data-file-height="300"></td>
<td><span xml:lang="en"><i>This file is published under the following <a href="https://en.wikipedia.org/wiki/Creative_Commons" title="w:Creative Commons">Creative Commons</a> license:<br>
<a href="//creativecommons.org/licenses/by-nc/3.0/">Attribution <b>NonCommercial</b> Unported 3.0</a></i></span></td>
</tr>
</tbody></table>
<div style="font-family:arial">

Upvotes: 1

Related Questions