Saving Audio to localStorage for Playback

10,396

Solution 1

This is not what localStorage is meant for. localStorage can only store strings and can only accommodate a small amount of date.

If you're really determined to do this you need to:

Note that Base64 encoding usually increases the size of the source material by about 33%. This is because it can take any kind of data, including binary, and converts it to a string made of a 64 character set that's compatible with standard ASCII strings.

To play it back, you need to the opposite

  • retrieve the Base64 encoded string from localStorage
  • decode the Base64 string into an ArrayBuffer
  • load the ArrayBuffer into an Audio object
  • assign the Audio object to an <audio> element

It should also be noted that localStorage is often only given 5-10MB of storage space, depending on the browser. This can be tested here, but generally, keep your mp3 files small by encoding them with lower bit rates, use mono instead of stereo channels where possible, and reduce sample rate if the audio is just spoken words and not music.

Solution 2

localStorage is not meant to store such data. Instead, you should use the IndexedDB API, which is made for storing this kind of data.

Dealing with the indexedDB is highly simplified with the Mozilla's localForage lib, which provides a syntax similar to the one of localStorage.

Fiddle since StackSnippets® doesn't allow access to indexedDB nor localStorage.


But to convert a File (or a Blob) to a String, as needed by the localStorage API, you can use an FileReader and its readAsDataURL() method, which will provide an dataURI, directly playable by MediaElements.

f.onchange = e =>{
  if(f.files[0].type.indexOf('audio/') !== 0){
    console.warn('not an audio file');
    return;
    }
  const reader = new FileReader();
  reader.onload = function(){
    var str = this.result;
    // this is a string, so you can store it in localStorage, even if it's really not a good idea
    console.log(str);
    const aud = new Audio(str);
    aud.play();
    };
  reader.readAsDataURL(f.files[0]);
  }
<input type="file" id="f">
Share:
10,396

Related videos on Youtube

RockGuitarist1
Author by

RockGuitarist1

I am a front end web developer, who at work, focuses mainly on Angular applications. Outside of work, I design/develop websites for friends and those who ask for my services. I am also a professional musician who goes on tour from time to time.

Updated on September 27, 2022

Comments

  • RockGuitarist1
    RockGuitarist1 over 1 year

    I am trying to create a function in which an mp3 file is pulled from

    <input type="file" id="sound" accept="audio/*"/>
    

    and stored in localStorage within Chrome. (I know localStorage is limited in size, but the audio files that I will be working with will be under 2mb.) Once this audio file is stored, it will be played with:

    var audio = new Audio('audioFile');
    audio.play();
    

    I am still new to web development, so any pointers would be great!

    NOTE: This is not a duplicate of HTML5 record audio to file. I am trying to take a preexisting mp3 file from my HDD and storing it into localStorage so that it can be played back.

    • RockGuitarist1
      RockGuitarist1 almost 7 years
      I've scoured for a few hours before resorting to here. I prefer to try to figure out a process before coming here to get yelled at.
  • RockGuitarist1
    RockGuitarist1 almost 7 years
    This is what I am looking for. Thank you!
  • Kaiido
    Kaiido almost 7 years
    Wow don't go through an ArrayBuffer and don't do the b64 conversion yourself... You can simply use an FileReader and its readAsDataURL method. And you don't need to pass the dataURI to an arrayBuffer either to play it, you can simply pass the b64 dataURI into the Audio element. And you'd have to explain what is an Audio object if it's not an AudioElement.
  • Sami Ahmed Siddiqui
    Sami Ahmed Siddiqui almost 7 years
    @Kaiido write another answer with a complete explanation if you have a better way of doing this or add what you've said to mine as an edit since you have enough rep.
  • RockGuitarist1
    RockGuitarist1 almost 7 years
    I wish I could use this, but sadly whenever I add localforage into the script source, I get this error: "Refused to load the script because it violates the following Content Security Policy directive: "script-src 'self' syndication.twitter.com"."
  • Kaiido
    Kaiido almost 7 years
    @RockGuitarist1, are you trying to load the script from rawgit too ? Your CSP seems to be configured to allow only scripts form the same origin as your page and from twitter. So if you download the script on your server, and load it from your server, it should work.
  • RockGuitarist1
    RockGuitarist1 almost 7 years
    I actually downloaded localforage and imported it into the project. I then used: <script src="../localforage/dist/localforage.js"></script> <script>localforage.getItem('something', myCallback);</script> and it still threw the error. I am actually working on a open sourced Chrome extension.
  • Kaiido
    Kaiido almost 7 years
    Then I guess you'd have to include it in your extension manifest, but my knowledge on extensions is very limited. Note that there is also probably an even better API for saving files from extensions, you've got access to far more powerful APIs than us poor web-devs. But this would change completely your question.
  • RockGuitarist1
    RockGuitarist1 almost 7 years
    Sounds like that is something I would have to do. I have gotten the previous method to work the way I wanted it to, but this method would've been better since I can manipulate audio volume levels.
  • Kaiido
    Kaiido almost 7 years
    Don't use the first answer's solution at all. In the worth case, you can use my second snippet, but it's really not a good idea. And once again, since you're working on a chrome extension, look for chrome:// APIs e.g the storage API may work for you.