<h2>Preparation</h2>
<ol>
  <li>Set your REAPER timeline to <strong>Seconds</strong></li>
  <li>Export Markers and Regions list to CSV with <strong>X-Raym_Export markers and regions as tab-delimited CSV file.lua</strong></li>
  <li>Export full audio of the project (from position 0)</li>
</ol>
<hr>
<div id="message"></div>
<div id="inputs">
  <h2>Input</h2>
  <ol>
  <li>
  <label for="input-audio">Audio File: </label>
  <input id="input-audio" type="file" accept="audio/*"/>
  </li>
  <li>
  <label for="file-csv">Marker and Region File: </label>
  <input id="file-csv" type="file" accept=".csv">
  </li>
  <li>
  <label for="preroll">Pre-Roll when clicked (in seconds): </label>
  <input id="preroll" type="number" min="0" max="10" value="0" step="0.5">
  </li>
  </ol>
</div>
<div id="output">
  <h2>Output</h2>
  <div id="bar">
    <div id="bar-audio">
      <audio id="audio" controls src="https://greghub.github.io/green-audio-player/examples/audio/example-1.mp3"></audio>
    </div>
    <button id="fullscreen">⛶</button>
    <progress id="progress" value="0"></progress>
    <div id="progressbar"></div>
  </div>
  <div id="buttons"></div>
</div>
* {
  font-family: Arial;
  box-sizing: border-box;
}

body {
  padding 0;
  margin: 0;
  line-height:1.5em;
  background-color: #1d1d1d;
  color: white;
}

code {
  font-family: "Courier";
}

h2, ol, label {
  margin-left: 1em;
}

#message {
  display: none;
}

#inputs {
  margin-bottom: 1em;
}

#bar {
  display:flex;
  box-shadow: 0 0 10px 0px black;
  background-color: #1d1d1d;
  padding:0;
  margin:0;
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  flex-wrap: wrap;
  z-index: 100;
}

#bar-audio {
  width: 90%;
  background-color: #1D1D1D;
  color: white;
  border-radius: 0px;
  box-shadow: unset;
}

#progressbar svg path:first-child {
  stroke: #d1d1d1!important;
}

#audio {
  display:none;
}

.play-pause-btn__icon, .volume__speaker {
  fill: white;
}

.controls__current-time, .controls__total-time {
  color: white;
}

#fullscreen {
  background: #1D1D1D;
  margin: 0;
  width: 10%;
  padding: 0px 10px;
  border:none;
  font-weight: bold;
  color: white;
  font-size:1.7em;
}

#fullscreen:hover{
  cursor: pointer;
}

progress[value] {
  /* Reset the default appearance */
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 2em;
  border: 0;
  display:none;
}

#progressbar {
  height: 2em;
}

.marker {
  border: none;
  padding: 15px 20px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 2em;
  width: 100%;
  border-left: 10px solid black;
  border-right: 10px solid black;
}

.marker:hover {
  cursor:pointer;
  opacity: 0.9;
}

.marker:active  {
  cursor:pointer;
  opacity: 0.5;
}

.marker.current {
  border-left: 10px solid white;
  border-right: 10px solid white;
  opacity:1;
  padding: 15px 20px;
}
"use strict";

console.clear()

new GreenAudioPlayer('#bar-audio');

document.getElementById('fullscreen').addEventListener('click', () => {
	if (screenfull.enabled) {
		screenfull.toggle();
	}
});

// Automatic Color According to background
// Mod of http://jsfiddle.net/QkSva/
// http://stackoverflow.com/questions/6750695/jquery-change-text-color-based-on-containing-elements-background-color
function isDark(color) {
  var match = /rgb\((\d+).*?(\d+).*?(\d+)\)/.exec(color);
  return (match[1] & 255) + (match[2] & 255) + (match[3] & 255) < 3 * 256 / 2;
}

function hexToRgb(hex) {
  var bigint = parseInt(hex, 16);
  var r = (bigint >> 16) & 255;
  var g = (bigint >> 8) & 255;
  var b = bigint & 255;

  return "rgb(" + r + "," + g + "," + b + ")";
}

// This is even better for desktop but don't work on mobile
document.getElementById("input-audio").addEventListener(
  "change", function(e) {
  audio.src = URL.createObjectURL(this.files[0]);
});

var buttons_container = document.getElementById("buttons");
var audio = document.getElementById("audio");

window.setInterval(CheckCurrentMarker, 50)

var markers = [];
var height = document.getElementById('bar').offsetHeight;
var options = { offset: height, easing: 'easeInOutCubic', durationMin: 300 }
var scroll = new SmoothScroll('a[href*="#"]', options);
var previous_marker;
var buttons_div = document.getElementById('buttons');
var progress = document.getElementById('progress');
var preroll = document.getElementById('preroll');
var progressbar = document.getElementById('progressbar');

var bar = new ProgressBar.Line(progressbar, {
  trailColor: '#1d1d1d',
  svgStyle: {width: '100%', height: '100%'}
});

// If the page has been saved to new file with already rendered markers
document.addEventListener("DOMContentLoaded", function(event) {
  GetMarkers();
});

buttons_div.addEventListener("click", function(e) {
  let time = e.target.dataset.start;
  let offset = preroll.value;
  if( time === undefined ) {
    time = e.target.parentNode.dataset.start - offset
  }
  audio.currentTime = parseFloat(time);
});

// Bind Continuous Play
// audio.addEventListener("timeupdate", CheckCurrentMarker)

document.getElementById("file-csv").addEventListener(
  "change",
  function(evt) {
    var file = document.getElementById("file-csv").files[0];
    if (file) {
      Papa.parse(file, {
        complete: function(results) {
          buttons_container.innerHTML = "";
          var out = "";
          results.data.forEach(function(elm, i) {
            if (i > 0 && elm[1]) {
              var col = elm[5] === "0" ? col = "#7d7d7d" : col = elm[5];
              if( col.charAt(0) != '#' ) col = "#" + col
              var is_dark = isDark(hexToRgb(col));
              var color = is_dark ? "#FFFFFF" : "#000000";
              var height = elm[4] > 0 ? elm[4] * 10 : "100%";
              out =
                out +
                '<button class="marker" style="color:' +
                color +
                ";background-color: " +
                col +
                //";height:" +
                //height +
                //'px" data-start="' +
                '" data-start="' +
                elm[2] +
                '" data-end="' +
                elm[3] +
                '"><p>' +
                elm[1] +
                "</p></button>";
            }
          });
          
          buttons_container.innerHTML = out;
          GetMarkers();
          
        }
      });
    }
  },
  false
);

function GetMarkers() {
  var buttons = buttons_container.getElementsByTagName("button");
  for (var i = 0; i < buttons.length; i++) {
    markers[i] = {
      time_start: parseFloat(buttons[i].dataset.start),
      time_end: parseFloat(buttons[i].dataset.end),
      dom: buttons[i],
      color: buttons[i].style.backgroundColor,
      id: i
    }

  }
}

var last_marker = {id: 0};
audio.addEventListener("seeked", ResetLastMarker)

function ResetLastMarker() {
  last_marker = {id: 0}
}

function CheckCurrentMarker() {
  var current_time = audio.currentTime;
  markers.forEach(function(marker, i) {
    if (current_time >= marker.time_start && current_time <= marker.time_end&& i>= last_marker.id) {
      last_marker = marker;
      marker.dom.classList.add("current");
      let val = (current_time-marker.time_start) / (marker.time_end - marker.time_start)
      if( isNaN(val) ) return false;
      bar.path.setAttribute('stroke', marker.color);
      /*if( isDark(marker.color) ) {
        progressbar.className = 'white';
      } else {
        progressbar.className = '';
      };*/
      bar.set((current_time-marker.time_start) / (marker.time_end - marker.time_start));
    } else {
      marker.dom.classList.remove("current");
    }
  }); // For each markers
  if( last_marker !== previous_marker && last_marker.id !== 0) {
    scroll.animateScroll(last_marker.dom);
  }
  previous_marker = last_marker;
}

External CSS

  1. https://cdn.jsdelivr.net/gh/greghub/green-audio-player/dist/css/green-audio-player.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/PapaParse/4.3.7/papaparse.js
  2. https://cdnjs.cloudflare.com/ajax/libs/smooth-scroll/16.1.0/smooth-scroll.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/screenfull.js/4.2.1/screenfull.min.js
  4. https://cdnjs.cloudflare.com/ajax/libs/progressbar.js/1.0.1/progressbar.min.js
  5. https://cdn.jsdelivr.net/gh/greghub/green-audio-player/dist/js/green-audio-player.min.js