Showing posts with label javascript tutorial. Show all posts
Showing posts with label javascript tutorial. Show all posts

Tuesday, August 30, 2016


Dear Developers, today I'll guiding you to creating an MP3 Player with HTML programming step-by-step. Let's go!
What is HTML5 Audio Player? It is a subject of the HTML5 specification, incorporating audio input, playback, and synthesis, as well as speech to text, in the browser, given rise to some new and exciting possibilities, especially when it comes to music related web applications... etc.
We hope to introduce you to some of these possibilities by walking you through how I created this jam station. This project originally began as an experiment, but over time evolved into an open ended practice and teaching tool for guitar players.
A solid understanding of Javascript fundamentals is necessary for following this tutorial.
If you aren’t particularly interested in recreating this tutorial exactly, that’s all right. Much of what I cover in this tutorial is applicable to different types of musical applications.
A basic grasp on music theory will help you in following this tutorial. An understanding of time signatures, measures and beats are a must.

Things You Will Need

The audio track you choose will need to be recorded to a click track or metronome. In other words, the length of each beat will need to be uniform across the entire track.
There are a few things you will need to know about your audio track in order to find the current beat and measure.
  • Beats Per Minute (BPM or Tempo)
  • Time Signature
  • Time the Track Starts
  • How many measures are used to count in (not always applicable)
You can use the audio track I’ve used in the CodePen demo (right click to save) if you’d like.
Here’s the relevant data for that track.
  • BMP = 117
  • Time Signature = 4/4
  • Start time of the first beat = 0.2s
  • Measures used for count in = 1
If you are using your own track and you are unsure of some of these specifics, a free audio editor like Audacity should help. It won’t be able to tell you everything, but you will be able to find the exact start time of the first beat. If you are recording your own track it’s best to make note of this information from the start.

Markup and CSS

Before getting into the fun stuff, here’s HTML and CSS we’ll be using.
     <div class="wrapper">
       <div id="chord-diagram"></div>
       <audio id="jam-track"  src="https://myguitarpal.com/wp-content/uploads/2014/09/12-Bar-Blues-in-A-Version-1.mp3" controls></audio>
      <br>

      <label>Beat: </label>
      <div class="data" id="beat"></div>

      <label>Measure: </label>
      <div class="data" id="measure"></div>

      <label>Section: </label>
      <div class="data" id="section"></div>

      <label>Chord: </label>
      <div class="data" id="chord"></div>

      <div id="chord-progression"></div>
    </div>

   .wrapper {
      max-width: 400px;
    }

    #chord-progression {
      margin-top: 20px;
      padding-top: 20px;
      border-top: 1px solid lightgray;
    }

    .section {
      margin-bottom: 20px;
      display: none;
    }

    .measure {
      width: 25%;
      display: inline-block;
    }

    .m-line {
      float: right;
      width: 10px;
    }

    audio {
      width: 100%;
    }

    .data {
      display: inline;
      margin-right: 10px;
    }

    label {
      font-weight: bold;
    }

Set up the Variables

First we’ll take all of the track data we discussed and set up some variables.
    // beats per minute
    var BPM = 117;

    // beats per second
    var BPS = 60 / BPM;

    // measures used for count in
    var measuresCount = 1;

    // time the track starts
    var offsetSeconds = 0.2;

    // time signature
    var timeSigTop = 4;
    var timeSigBottom = 4;

Chord Progression

I’ve kept the chord progression data model pretty simple for the purposes of this tutorial and used a multidimensional array.
The top level array is the chord progression sections. These could be verses, choruses, bridges, etc.
The nested arrays contain the chords for each measure in their respective sections.
    var sectionOne = ['A7', 'A7', 'A7', 'A7', 'D7', 'D7', 'A7', 'A7', 'E7', 'D7', 'A7', 'E7'];
    var sectionTwo = ['A7', 'A7', 'A7', 'A7', 'D7', 'D7', 'A7', 'A7', 'E7', 'D7', 'A7', 'A7', 'A7', 'A7', 'A7', 'A7', 'A7', 'A7'];

    var chordProgression = [
      sectionOne,
      sectionOne,
      sectionOne,
      sectionTwo
    ];
Music often repeats certain sections like the chorus, so in order to keep things DRY, store each section like shown above in a variable, then include the sections in the chordProgression array.

Running the Jam Station

This jam station uses the timeupdate event which is similar to using a game loop. Each time the timeupdate event fires (several times per second while the track is playing) we’re going to run a function and update some data such as the current beat, measure and chord. The only time the data will not update is when the track is paused.
When the timeupdate event fires we’ll run the jamStation function. The function will be called several times per second while the audio is playing.
    var audio = document.getElementById('jam-track');

    audio.ontimeupdate = function() {
      jamStation();
    };

Fun Part

This function should only deal with data, not presentation. We’ll use another function later (renderJamStation) to deal with presentation.
To find the current beat we will use the expression (audio.currentTime - offsetSeconds + BPS) / BPS, and we’ll store this value in the variable beat.
We can then find the current measure with the expression (beat - 1) / timeSigTop, which we’ll store in the variable measure.
    function jamStation() {
      var beat = (audio.currentTime - offsetSeconds + BPS) / BPS; 
      var measure = (beat - 1) / timeSigTop;
    }
The beat and measure variables should now be rounded down. This will give us easier numbers to work with when we are doing comparisons. I wouldn’t suggest doing this however if you were to do a more complex app where you needed to use fractions of a beat.
We’ll store these whole numbers in cleanBeat and cleanMeasure.
If you want to display the current beat in a measure you can use the following expression: ((cleanBeat - 1) % timeSigTop) + 1. Let’s say we’re on beat 13. The expressions values would then be: ((13 - 1) % 4) + 1. We add 1 because there is no such thing as beat 0.
Now instead of having the beat increment infinitely it will only increment as high as the number in the top of the time signature. measureBeat will contain the value of the beat within the measure. So, cleanMeasure will count 1,2,3,4,5,6,7,8, etc. whereas measureBeat will count 1,2,3,4,1,2,3,4 etc.
    function jamStation() {
      var beat = (audio.currentTime - offsetSeconds + BPS) / BPS; 
      var measure = (beat - 1) / timeSigTop;

      // round down for beat and measure
      var cleanBeat = Math.floor(beat);
      cleanMeasure = Math.floor(measure);

      // find the current beat within the measure
      measureBeat = ((cleanBeat - 1) % timeSigTop) + 1;
    }

Don’t put var in front of cleanMeasure as we’ve already declared this variable outside of the function (see the CodePen) as we want to access this value from outside of jamStation().
At this point we actually have the most important data we’ll need. The tricky part though is going to be dealing with the chord progression.
The next part of the jamStation function is for determining two things: the currentSection and the currentChord.
First we need to use an if…else statement to tests if we are past the first measure. If we are, we will find the value of the current section and current chord. If we aren’t, currentSection and currentChord will be set to null.
To find the current section and current chord we loop through the sections, then run a nested loop that loops through the measures in each section.
The variable count is set and incremented by 1 every time we loop through a measure of each section. Now, if cleanMeasure equals count, we know we’ve found the section and measure we’re on. Because we’ve found the correct section and measure the audio track is currently at, we need store those values and we need to break both loops so the correct data doesn’t get overwritten the next time the loop runs.
Don’t use var in front of currentSection or currentChord, as again, these variables have been declared outside of jamStation() so we can share them between functions.
    function jamStation() {
      var beat = (audio.currentTime - offsetSeconds + BPS) / BPS; 
      var measure = (beat - 1) / timeSigTop;

      // round down for beat and measure
      var cleanBeat = Math.floor(beat);
      cleanMeasure = Math.floor(measure);

      // find the current beat within the measure
      measureBeat = ((cleanBeat - 1) % timeSigTop) + 1;

      if (cleanMeasure > 0) {
        // find the currentSection and currentChord
        var count = 0;
        var br = false;
          for (var s = 0; s < chordProgression.length; s++) {
            for (var m = 0; m < chordProgression[s].length; m++) {
              count++;
              if (cleanMeasure == count) {
                currentSection = s + 1;
                currentChord = chordProgression[s][m];
                br = true;
                break;
              }
            }
            if (br === true) {
              break;
            }
          }
      } else {
        currentSection = null;
        currentChord = null;
      }

      // display the jam station and its data
      renderJamStation();
    }
Now we have all of the data we need and it’s accessible globally.
At the end of the jamStation function you can see that renderJamStation() is run. This function will be used for presentation purposes only and we will cover it in a moment.

Rendering the Chord Progression

We need to display the chord progression. Let’s wrap it in a function called renderChordProgression to keep things tidy. This function will only be run once as the chord progression data is never updated.
First we loop through the sections in the chord progression. We create a div with the class of “section” and id of “section-[number]” each time the loop loops. Each section has a unique id so we can display that specific section when it is being played.
    // take the chordProgression array and render the HTML
    function renderChordProgression() {
      var progression = document.getElementById('chord-progression');

      // make the sections
      for (var s = 0; s < chordProgression.length; s++) {
        progression.innerHTML += '<div class="section" id="section-' + (s + 1) + '"></div>';
      }
    }
Next we loop through the sections again so we can then loop through all the measures in a nested loop. Each measure will then be included in it’s respective section.
You probably noticed the count variable was set and incremented. This is done in order to increment through every measure in every section.
The complete function looks like this:
    // take the chordProgression array and render the HTML
    function renderChordProgression(){
      var progression = document.getElementById('chord-progression');

      // make the sections
      for (var s = 0; s < chordProgression.length; s++){
        progression.innerHTML += '<div class="section" id="section-' + (s + 1) + '"></div>';
      }

      var count = 0;

      for (var s = 0; s < chordProgression.length; s++) {
        for (var m = 0; m < chordProgression[s].length; m++) {
          count++;
          var section = document.getElementById('section-' + (s + 1));
          section.innerHTML += '<div class="measure" id="measure-' + count + '">' 
                            + chordProgression[s][m] 
                            + '<div class="m-line">|</div></div>';
        }
      }
    }

You will probably want to style the chord progression. You can reference the CSS in the CodePen at the top of this post.

Presentation now

The function renderJamStation is called from inside the function jamStation.
    function renderJamStation() {

      // show the beat within the measure, not overall
      document.getElementById('beat').innerHTML = measureBeat;

      // show the current measure, but only if the jam track is past the count in measures
      var measureElem = document.getElementById('measure');

      // only show the current measure if it's > 0
      if (cleanMeasure > 0) {
        measureElem.innerHTML = cleanMeasure;
      } else {
        measureElem.innerHTML = '';
      }

      // show the section number
      document.getElementById('section').innerHTML = currentSection;
      // show the current chord name
      document.getElementById('chord').innerHTML = currentChord;

      // hide all sections before displaying only the section we want to see
      var allSections = document.getElementsByClassName('section');

      for (var i = 0; i < allSections.length; i++) {
        allSections[i].style.display = 'none';
      }

      // show the currently playing section
      if (currentSection != null) {
        document.getElementById('section-' + currentSection).style.display = 'block';
      } else {
        allSections[0].style.display = 'block';
      }

      // style the current chord in the chord progression
      if (cleanMeasure > 0) {
        // style all measures black
        var measures = document.getElementsByClassName('measure');
        for (var i = 0; i < measures.length; i++) {
           measures[i].style.color = 'black';
        }
        // style current measure red
        document.getElementById('measure-' + cleanMeasure).style.color = 'red';
      }
    }

I won’t cover every part of this function as most of it should be self-explanatory if you’re familiar with JavaScript. For the most part, it takes some data such as the current chord and current measure and displays it.
The main bit of this function you should look closely at is how it displays the right section.
    // hide all sections before displaying only the section we want to see
    var allSections = document.getElementsByClassName('section');

    for (var i = 0; i < allSections.length; i++) {
      allSections[i].style.display = 'none';
    }

    // show the currently playing section
    if (currentSection != null) {
      document.getElementById('section-' + currentSection).style.display = 'block';
    } else {
      allSections[0].style.display = 'block';
    }

You can see every section is first hidden. If this isn’t done, each section will appear when it’s time is ready, but the sections which aren’t playing will still be visible.
To display the current section being played we select it by its ID and set its display property back to block.
If currentSection is null, we show the first section. If we don’t do this, the first section will not be visible before playing the track and arriving at a point in time where the section should be visible.

Rendering the Initial Display

Finally, we run two functions. renderChordProgression() needs to be run once in order to render the chord progression and jamStation() should be run once as well. Yes, the function jamStation() will be run whenever timeupdate fires, but it should also be run once on its own, otherwise the chord progression will not render at first.
    renderChordProgression();
    jamStation();

Further Thoughts and Ideas

If you are interested in displaying chord diagrams for an instrument, you have a lot of the data you need, most importantly the chord name and type.
Let’s say you wanted to display chord diagram images when the chord progression was on the appropriate chord. We already have the current chord stored in the variable currentChord.
You could create an array of chord objects.
    var chords = [
      {
        name: 'A',
        type: '7',
        src: '/images/a7.jpg',
      },
      {
        name: 'D',
        type: '7',
        src: '/images/d7.jpg',
      },
      {
        name: 'E',
        type: '7',
        src: '/images/e7.jpg',
      }
    ];

Then, run this bit of logic inside the jamStation function which will display the right chord at the right time.
    for (var i = 0; i < chords.length; i++) {
      if (chords[i].name + chords[i].type == currentChord) {
        document.getElementById('chord-diagram').innerHTML = '<img src="' + chords[i].src + '"/>';
      }
    }

Conclusion

Maybe you don’t want to build exactly what I’ve covered today and that’s OK. I can think of so many ways you could apply what I’ve covered in this article to all different types of projects. Here are a few ideas:

  • A new take on galleries or sliders. Transition images on certain beats or measures.
  • Animate images to a beat. Tapping feet? Poses of someone dancing?
  • Audio visualizations.
  • Annotations or diagrams used for teaching purposes which are synced to a piece of music.
  • There are all sorts of things you could do with this amazing, open source JavaScript music notation API

Suggest for you:


How To Creating an MP3 Player with HTML5 Step-By-Step

Read More

Copyright © WISE CODE DECK | Designed With By Blogger Templates
Scroll To Top