Saturday, March 25, 2023

Step Sequencer

<header>
  <button id="play-pause" data-playing="false"><i class="fa fa-play"></i></button>
  <label>This is a javascript lab created by <a href="http://futuristicandco.org" target="new">Kamogelo Matshego</a></label>
</header>
<div class="container player"></div>
<audio id="1" src="https://freewavesamples.com/files/Bass-Drum-1.wav"></audio>
<audio id="2" src="https://freewavesamples.com/files/Ensoniq-ESQ-1-Snare.wav"></audio>
<audio id="3" src="https://freewavesamples.com/files/Closed-Hi-Hat-1.wav"></audio>
<audio id="4" src="https://freewavesamples.com/files/Ensoniq-SQ-1-Open-Hi-Hat.wav"></audio>
<audio id="5" src="https://freewavesamples.com/files/Floor-Tom-1.wav"></audio>
*, *::before, *::after {

  -webkit-box-sizing: border-box;
  box-sizing: border-box;

}

*:before, *:after {

  content: '';

}

html, body {

  margin: 0px;
  padding: 0px;

}

body {

  background: #16181C;
  font-family: 'Open Sans', sans-serif;

}

div {

  display: block;
  position: relative;

}

header {

  width: 1300px;
  height: auto;
  padding: 50px 0px;
  margin: 0px auto;
  position: relative;

}

header label {
  
  color: white;
  font-size: 12px;
  margin-left: 20px;
  
}

header label a {
  
  color: #60B5D1;
  
}

.container {

  left: 50%;
  z-index: 0;
  float: left;
  width: 1100px;
  height: auto;
  margin-left: 100px;
  max-height: 100vh;
  position: relative;
  border: 8px solid #16181C;
  transform: translateX(-50%);

}

.row {

  float: left;
  clear: both;
  width: 100%;
  height: auto;
  max-width: 100%;
  border-bottom: 8px solid #16181C;

}

.row::before {

  top: 0px;
  left: -208px;
  width: 200px;
  height: 100%;
  position: absolute;
  letter-spacing: 1px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.025);
  border-right: 8px solid #16181C;
  box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.65),
              inset 0px 0px 0px 2px rgba(255, 255, 255, 0.035),
              inset 0px 5px 20px -10px rgba(255, 255, 255, 0.25),
              inset 0px -5px 20px -10px black;

}

.row::after {

  top: 15%;
  left: -198px;
  width: 172px;
  height: 70%;
  line-height: 8px;
  z-index: 1;
  padding: 15px;
  position: absolute;
  font-family: 'Open Sans';
  font-weight: 300;
  text-transform: uppercase;
  color: rgba(255,255,255,0.35);
  font-size: 11px;
  border-radius: 2px;
  background: #101114;
  border: 1px solid rgba(0, 0, 0, 0.5);
  box-shadow: inset 0px 0px 0px 1px rgba(255, 255, 255, 0.035),
              inset 0px -20px 20px -20px rgba(0, 0, 0, 0.5),
              inset 0px 20px 20px -20px rgba(255, 255, 255, 0.05);

}

.row:nth-child(1)::after {

  content: 'Drum Kick';

}

.row:nth-child(2)::after {

  content: 'Drum Snare';

}

.row:nth-child(3)::after {

  content: 'Drum Hi-Hat';

}

.row:nth-child(4)::after {

  content: 'Drum Hi-Hat 2';

}

.row:nth-child(5)::after {

  content: 'Drum Tom';

}

.sample {

  float: left;
  border-radius: 2px;
  display: inline-block;
  background: rgba(0, 0, 0, 0.025);
  border-right: 8px solid #16181C;
  box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.65),
              inset 0px 0px 0px 2px rgba(255, 255, 255, 0.035),
              inset 0px 5px 20px -10px rgba(255, 255, 255, 0.25),
              inset 0px -5px 20px -10px black;

}

.sample:nth-child(8n+6)::after {

  top: 1px;
  left: 1px;
  z-index: -1;
  opacity: 0.05;
  border-radius: 2px;
  background: #FF7B5F;
  position: absolute;
  width: calc(400% + 22px);
  height: calc(100% + 0px);

}

.sample::before {

  top: 15%;
  left: 15%;
  width: 70%;
  height: 70%;
  z-index: 1;
  position: absolute;
  border-radius: 2px;
  background: #101114;
  border: 1px solid rgba(0, 0, 0, 0.5);
  box-shadow: inset 0px 0px 0px 1px rgba(255, 255, 255, 0.035),
              inset 0px -20px 20px -20px rgba(0, 0, 0, 0.5),
              inset 0px 20px 20px -20px rgba(255, 255, 255, 0.05);

}

.sample:active::before,
.sample:focus::before {

  background: #0C0D0F;
  box-shadow: inset 0px 0px 0px 1px rgba(255, 255, 255, 0.035),
              inset 0px 20px 20px -20px rgba(0, 0, 0, 0.5),
              inset 0px 20px 20px -20px rgba(255, 255, 255, 0.05);

}

.sample.active::before {

  background: #DB6B53;
  border: 1px solid rgba(255, 255, 255, 0.035);
  box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.05),
              inset 0px -20px 20px -20px rgba(0, 0, 0, 0.85),
              inset 0px 20px 20px -20px rgba(255, 255, 255, 0.05);

}

.sample.active.hit::before {

  background: #DB6B53;
  border: 1px solid rgba(255, 255, 255, 0.15);
  box-shadow: 0px 0px 20px 1px #DB7E53,
              inset 0px 0px 0px 1px rgba(0, 0, 0, 0.05),
              inset 0px -20px 20px -20px rgba(0, 0, 0, 1),
              inset 0px 20px 20px -20px rgba(255, 255, 255, 1);

}

.sample.active.disabled.hit::before {

  background: #DBBBB0;
  border: 1px solid rgba(255, 255, 255, 0.035);
  box-shadow: inset 0px 0px 0px 1px rgba(0, 0, 0, 0.05),
              inset 0px -20px 20px -20px rgba(0, 0, 0, 0.85),
              inset 0px 20px 20px -20px rgba(255, 255, 255, 0.05);

}

.sample.active.disabled::before {

  opacity: 0.35;
  background: #DBBBB0;

}

.enable-disable-row {

  top: 50%;
  left: -60px;
  width: 14px;
  height: 14px;
  outline: none;
  border: none;
  z-index: 2;
  margin-top: -7px;
  background: #81B55B;
  position: absolute;
  border-radius: 50%;
  box-shadow: 0px 0px 0px 2px rgba(0, 0, 0, 0.5),
              0px 0px 0px 3px rgba(255, 255, 255, 0.15),
              0px 0px 0px 4px rgba(0, 0, 0, 0.5),
              inset 0px 0px 5px -1px white;

}

.enable-disable-row.disabled {

  background: #CF472F;

}
/**
 * Javascript Mixtape Drum Machine
 * @author Kamogelo Matshego
 * @version 1.0.0
 * @desc This is a javascript lab created
 * to demonstrate music patterns in the web browser.
 * I started this project just for fun and intend on
 * adding more to it and keeping this pen updated.
 * All drum samples were provided by 99Samples: 
 * http://99sounds.org/drum-samples/
 */

(function(DrummerJS, $, undefined) {

    'use strict';

    // ======================================
    // establish public variables and methods
    // ======================================

    DrummerJS.title = 'Javascript Drum Machine';
    DrummerJS.description = 'Create audio loops using drum  samples.'
    DrummerJS.author = 'Kamogelo Matshego';

    var $_container = $('.player');
    var $_containerWidth  = $_container.width();
    var $_containerHeight = $_container.height();

    var $_setBPM = $('#set-bpm');
    var $_playPauseBtn = $('#play-pause');
    var $_setSamplesBtn = $('#set-samples');

    var $_toggleRowBtn;
    var $_sampleBtn;

    var _urlHash;

    var _sampleList = 'default';

    var _totalSteps = 16;
    var _totalRows  = 5;

    var _currentStep = 1;

    var _sampleRate = 44100;
    var _minuteInSeconds = 60;
    var _beatsPerMinute =  140;

    var _stepDelay;

    var calculateBPM = function() {

      _stepDelay = Math.round(((_sampleRate * _minuteInSeconds) / (_beatsPerMinute * _totalSteps)) / _totalSteps);

      return _stepDelay;

    };

    // =======================================================
    // creates namespace provider which helps isolate
    // implementated code from the global namespace, providing
    // a single point of access for functions and methods.
    // =======================================================
    // this keeps the code more organized and allows the code
    // to be combined into more logical sections.
    // =======================================================

    DrummerJS.handler = (function() {

        function _handler() {

            /**
             * @var _this
             * @desc in a 'non-strict' environment, 'this' is bound to
             *       the global scope (if it hasn't been bound to anything else).
             *       in 'strict' mode it is set to undefined. we store it in a
             *       variable to avoid scope conflicts.
             */

            var _this = this;

            var _isPlaying;

            this.playPauseHandler = function(button) {

              calculateBPM();

              var status = button.data('playing');

              if(status === 'true') {

                button.data('playing', 'false').html('<i class="fa fa-play"></i>');

                _this.stopAudio();

              }else {

                button.data('playing', 'true').html('<i class="fa fa-pause"></i>');

                _this.startAudio();

              }

            };

            this.pageLoad = function() {

              if(!window.location.hash) {

                window.location.hash = '!/';

              }

              _urlHash = window.location.hash.split('/');

            };

            this.toggleSample = function(sample) {

              sample.toggleClass('active');

            };

            this.toggleSampleHandler = function() {

              $('.sample').on('click', function() {

                _this.toggleSample($(this));

              });

            };

            this.updateSampleList = function(samples) {

              _sampleList = samples;

              console.log(_sampleList);

            };

            this.updateBPM = function(bpm) {

              _beatsPerMinute = bpm;

              calculateBPM();

              _this.stopAudio();
              _this.startAudio();

            };

            this.stopAudio = function() {

              $_sampleBtn.removeClass('hit');

              clearInterval(_isPlaying);

            };

            this.startAudio = function() {

              _isPlaying = window.setInterval(function() {

                  _this.playAudio()

                }, _stepDelay);

            }

            this.playAudio = function() {

              if(_currentStep < _totalSteps) {

                _currentStep++;

              }else if(_currentStep >= _totalSteps){

                _currentStep = 1;

              }

              $_sampleBtn.removeClass('hit');

              for(var i = 1; i < _totalRows + 1; i++) {

                var $_newSample = $('.sample[data-row="' + i + '"][data-column="' + _currentStep + '"]');

                if($_newSample.hasClass('active')) {

                  $('#' + i)[0].currentTime = 0;

                  if(!$_newSample.hasClass('disabled')) {

                    $('#' + i)[0].play();

                  }else{

                    $('#' + i)[0].pause();

                  }

                  $_newSample.addClass('hit');

                }

              }

            };

            this.createMatrix = function(rows, columns) {

              var sampleSize  = $_containerWidth / columns;

              for(var i = 1; i < rows + 1; i++) {

                $('<div class="row" id="row-' + i + '"></div>').appendTo('.container');

                $('<button class="enable-disable-row" id="toggle-row-' + i + '"></button>').prependTo('#row-' + i);

                for(var k = 1; k < columns + 1; k++) {

                  $('<div class="column sample" data-row="' + i + '" data-column="' + k + '" style="height:' + (sampleSize - 8) + 'px;width:' + sampleSize + 'px"></div>').appendTo('#row-' + i);

                }

              }

              $_toggleRowBtn = $('.enable-disable-row');
              $_sampleBtn = $('.sample');

              calculateBPM();

            };

            this.toggleRowBtnHandler = function(button) {

              button.toggleClass('disabled');

              var rowID = button.attr('id').replace('toggle-row-', '');

              $('.sample[data-row="' + rowID + '"]').toggleClass('disabled');

            };


            /**
             * @function init()
             * @desc initiates the DrummerJS global function
             *       creating an active instance of the script on the
             *       current site.
             */

            this.init = function() {

                // run functions here

                _this.pageLoad();

                _this.createMatrix(_totalRows, _totalSteps);
                _this.toggleSampleHandler();

                $_playPauseBtn.on('click', function() {

                  _this.playPauseHandler($(this));

                });

                $_toggleRowBtn.on('click', function() {

                  _this.toggleRowBtnHandler($(this));

                });

                $_setBPM.on('change', function() {

                  _this.updateBPM($(this).val());

                });

                $_setSamplesBtn.on('change', function() {

                  _this.updateSampleList($(this).val());

                });

                // start the drum machine
              
                $_playPauseBtn.click();

                return this;

            };

            // initiate the script!
            return this.init();

        }

        // create a new handler object
        return new _handler();

    }());

// assign DrummerJS to the global namespace
}(window.DrummerJS = window.DrummerJS || {}, jQuery));

Full stack architect and software programmer

Kamogelo is a full stack architect and software programmer with more than 11 years of programming and application development experience. Don’t hesitate to contact us. Remember, asking questions is always important and welcome.

Building from the experience of many profitable commercial products and led numerous developments on web, API, and data analytics projects in the medical, software, geographic, and financial services sectors. Kamogelo is a strong believer of architectural-driven development, with the emphasis of balance between theory and practice will generate deeper understanding; subsequently can develop a practical and resilient system.

While performing his senior software architect roles in the Futuristicandco.org,  Kamogelo helps to establish the AI Lab to expand into machine learning and AI business. Fully aware of the Blockchain impact on business, Kamogelo has established a strong technical knowledge on Hyperledger and Ethereum by successfully designed and built systems for the medical health records and tokens exchange.

Benny has a candidate H.Sc. in Computer Science from the University of the Witswatersrand, where thesis is on AI’s expert system & spatial reasoning technologies.



All Posts

Step Sequencer

\n ","scripts":["//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"],"stylesheets":[]}'...