<!DOCTYPE html>
<html lang=“en” >

<head>

<meta charset=“UTF-8”>

<link rel=“apple-touch-icon” type=“image/png” href=“https://cpwebassets.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png” />
<meta name=“apple-mobile-web-app-title” content=“CodePen”>

<link rel=“shortcut icon” type=“image/x-icon” href=“https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico” />

<link rel=“mask-icon” type=“image/x-icon” href=“https://cpwebassets.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg” color=“#111” />


<title>CodePen - MidiMessaging</title>




<style>
html,
body {
margin: 0;
padding: 0;
}

#main,
.dash {
display: flex;
width: 80%;
align-items: center;
justify-content: center;
margin: 10vmin auto;
flex-direction: column;
flex-grow: 5;
}

.dash {
/display:none;/
}

span,
span > em {
color: red;
background: #06C7BD;
position: relative;
text-align: center;
}

strong,
i {
font-weight: bold;
font-family: “Droid Sans Mono”, sans-serif;
}

em {
font-style: normal;
width: 60px;
height: 60px;
align-items: center;
justify-content: center;
float: left;
display: inline-flex;
background: #efefef;
border-radius: 60px;
text-align: center;
margin: 2px;
flex-direction: column;
overflow: hidden;
}

table {
width: 100%;
}

td {
text-align: center;
width: 33%;
}

button,
p {
width: 100%;
}

small {
font-size: 0.5rem;
}

.slider {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 400px;
height: 5px;
background: #d3d3d3;
outline: none;
opacity: 0.7;
}
</style>

<script>
window.console = window.console || function(t) {};
</script>



<script>
if (document.location.search.match(/type=embed/gi)) {
window.parent.postMessage(“resize”, “*”);
}
</script>


</head>

<body translate=“no”>
<div id=“main”>
<h5 id=“title”>Lower Volume, click text, click text again to pause</h5>
<div class=“sentence”>
<p class=‘playMe’></p>
<button>Change Text</button>
</div>
</div>
<div class=“dash”>
<strong></strong>
<label for=“range”>Tempo
<input id=“range” type=“range” class=“slider” min=“2” max=“170” step=“1” value=“6” oninput=“range_disp.value = range.value”//> <output id=“range_disp”></output> </label>
<table>
<thead>
<tr>
<th>Note</th>
<th>Frequency</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr>
<td>None</td>
<td>0</td>
<td>0</td>
</tr>
</tbody>
</table>
</div>
<script src=“https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-1b93190375e9ccc259df3a57c1abc0e64599724ae30d7ea4c6877eb615f89387.js”></script>


<script id=“rendered-js” >
class midiMusic {
constructor(base, text) {
this.el = document.querySelector(‘.playMe’);
this.audioContext = window.AudioContext || window.webkitAudioContext;
this.audio = true;
this.osc = true;
this.dash = document.querySelector(‘.dash’);
this.td = this.dash.querySelectorAll(‘tr’)[1].children;
this.notePanel = this.dash.querySelector(‘strong’);
this.position = -2;
this.playing = false;
this.base = base || 30;
this.scaleSet = [];
this.lettersl = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’, ‘w’, ‘x’, ‘y’, ‘z’];
this.lettersu = this.lettersl.map(e => {return e.toUpperCase();});
this.numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
this.symbols = [’ ‘, ‘-’, ‘?’, ‘,’, ‘/’, ‘.’, ‘!’];
this.allkeys = this.symbols.concat(this.lettersl, this.numbers, this.lettersu).join(’‘);
this.scale = {
‘C0’: [16.35, 2109.89],
‘C#0/Db0’: [17.32, 1991.47],
‘D0’: [18.35, 1879.69],
‘D#0/Eb0’: [19.45, 1774.2],
‘E0’: [20.6, 1674.62],
‘F0’: [21.83, 1580.63],
‘F#0/Gb0’: [23.12, 1491.91],
‘G0’: [24.5, 1408.18],
‘G#0/Ab0’: [25.96, 1329.14],
‘A0’: [27.5, 1254.55],
‘A#0/Bb0’: [29.14, 1184.13],
‘B0’: [30.87, 1117.67],
‘C1’: [32.7, 1054.94],
‘C#1/Db1’: [34.65, 995.73],
‘D1’: [36.71, 939.85],
‘D#1/Eb1’: [38.89, 887.1],
‘E1’: [41.2, 837.31],
‘F1’: [43.65, 790.31],
‘F#1/Gb1’: [46.25, 745.96],
‘G1’: [49, 704.09],
‘G#1/Ab1’: [51.91, 664.57],
‘A1’: [55, 627.27],
‘A#1/Bb1’: [58.27, 592.07],
‘B1’: [61.74, 558.84],
‘C2’: [65.41, 527.47],
‘C#2/Db2’: [69.3, 497.87],
‘D2’: [73.42, 469.92],
‘D#2/Eb2’: [77.78, 443.55],
‘E2’: [82.41, 418.65],
‘F2’: [87.31, 395.16],
‘F#2/Gb2’: [92.5, 372.98],
‘G2’: [98, 352.04],
‘G#2/Ab2’: [103.83, 332.29],
‘A2’: [110, 313.64],
‘A#2/Bb2’: [116.54, 296.03],
‘B2’: [123.47, 279.42],
‘C3’: [130.81, 263.74],
‘C#3/Db3’: [138.59, 248.93],
‘D3’: [146.83, 234.96],
‘D#3/Eb3’: [155.56, 221.77],
‘E3’: [164.81, 209.33],
‘F3’: [174.61, 197.58],
‘F#3/Gb3’: [185, 186.49],
‘G3’: [196, 176.02],
‘G#3/Ab3’: [207.65, 166.14],
‘A3’: [220, 156.82],
‘A#3/Bb3’: [233.08, 148.02],
‘B3’: [246.94, 139.71],
‘C4’: [261.63, 131.87],
‘C#4/Db4’: [277.18, 124.47],
‘D4’: [293.66, 117.48],
‘D#4/Eb4’: [311.13, 110.89],
‘E4’: [329.63, 104.66],
‘F4’: [349.23, 98.79],
‘F#4/Gb4’: [369.99, 93.24],
‘G4’: [392, 88.01],
‘G#4/Ab4’: [415.3, 83.07],
‘A4’: [440, 78.41],
‘A#4/Bb4’: [466.16, 74.01],
‘B4’: [493.88, 69.85],
‘C5’: [523.25, 65.93],
‘C#5/Db5’: [554.37, 62.23],
‘D5’: [587.33, 58.74],
‘D#5/Eb5’: [622.25, 55.44],
‘E5’: [659.25, 52.33],
‘F5’: [698.46, 49.39],
‘F#5/Gb5’: [739.99, 46.62],
‘G5’: [783.99, 44.01],
‘G#5/Ab5’: [830.61, 41.54],
‘A5’: [880, 39.2],
‘A#5/Bb5’: [932.33, 37],
‘B5’: [987.77, 34.93],
‘C6’: [1046.5, 32.97],
‘C#6/Db6’: [1108.73, 31.12],
‘D6’: [1174.66, 29.37],
‘D#6/Eb6’: [1244.51, 27.72],
‘E6’: [1318.51, 26.17],
‘F6’: [1396.91, 24.7],
‘F#6/Gb6’: [1479.98, 23.31],
‘G6’: [1567.98, 22],
‘G#6/Ab6’: [1661.22, 20.77],
‘A6’: [1760, 19.6],
‘A#6/Bb6’: [1864.66, 18.5],
‘B6’: [1975.53, 17.46],
‘C7’: [2093, 16.48],
‘C#7/Db7’: [2217.46, 15.56],
‘D7’: [2349.32, 14.69],
‘D#7/Eb7’: [2489.02, 13.86],
‘E7’: [2637.02, 13.08],
‘F7’: [2793.83, 12.35],
‘F#7/Gb7’: [2959.96, 11.66],
‘G7’: [3135.96, 11],
‘G#7/Ab7’: [3322.44, 10.38],
‘A7’: [3520, 9.8],
‘A#7/Bb7’: [3729.31, 9.25],
‘B7’: [3951.07, 8.73],
‘C8’: [4186.01, 8.24],
‘C#8/Db8’: [4434.92, 7.78],
‘D8’: [4698.63, 7.34],
‘D#8/Eb8’: [4978.03, 6.93],
‘E8’: [5274.04, 6.54],
‘F8’: [5587.65, 6.17],
‘F#8/Gb8’: [5919.91, 5.83],
‘G8’: [6271.93, 5.5],
‘G#8/Ab8’: [6644.88, 5.19],
‘A8’: [7040, 4.9],
‘A#8/Bb8’: [7458.62, 4.63],
‘B8’: [7902.13, 4.37] };

this.el.onclick = e => {
this.audio.resume();
this.playing = !this.playing;
this.play().then();
};
document.querySelector(‘button’).onclick = e => {
this.position = -1;
this.playing = true;
this.text = prompt(‘Type a sentence or parse song to listen to’) || ‘Oops! You forgot to pick some text.’;
this.play().then();
};
this.el.innerText = text;
this.text = this.allkeys;
this.scaleSet = this.setupScale(this.base, this.base + this.text.length);
this.notePanel.innerHTML = this.allkeys.split(’‘).map((a, b, c) => {let cn = a === ’ ’ ? ‘<sub>[space]</sub>’ : a;
return c[b] === this.currentNote() ? ‘<em>’ + cn + ‘<small>’ + this.scaleSet[this.allkeys.indexOf(a)] + ‘</small></em>’ : ‘<em>’ + cn + ‘<small>’ + this.scaleSet[this.allkeys.indexOf(a)] + ‘</small></em>’;
}).join(’‘);
this.buildOSC();
this.text = text || this.text;
this.cells = this.notePanel.querySelectorAll(‘em’);
}
setupScale(start, stop) {
let scaleSet = [];
Object.keys(this.scale).map((a, b, c) => {if (b >= start && b <= stop) scaleSet.push(a);});
return scaleSet;
}
buildOSC() {
this.audio = new this.audioContext();
this.osc = this.audio.createOscillator();
this.osc.type = ‘triangle’;
this.osc.connect(this.audio.destination);
this.osc.start(1);
this.osc.disconnect();
}
spanit(e) {return ‘<span>’ + e + ‘</span>’;}
updateText(note, freq) {
Array(note, freq, this.position).forEach((a, b) => {this.td[b].innerHTML = this.position >= 0 ? a : ‘’;});
this.el.innerHTML = this.text.substring(0, this.position) + this.spanit(this.currentNote()) + this.text.substring(this.position + 1, this.text.length);
let span = this.notePanel.querySelector(‘span’);
if (span) span.parentNode.innerHTML = span.innerHTML;
this.cells[this.scaleSet.indexOf(note)].innerHTML = this.spanit(’<em>’ + this.currentNote() + ‘<small>’ + note + ‘</small></em>’);
this.cells[0].remove();
}
currentFreq() {let freq = this.scale[this.scaleSet[this.allkeys.indexOf(this.currentNote())]] || false;return freq[0] || false;}
currentNote() {return this.text[this.position] || ‘’;}
resetPosition() {this.position = -1;this.playing = false;this.osc.disconnect();}
async play() {
this.position++;
if (this.position >= this.text.length) this.resetPosition();
this.updateText(this.scaleSet[this.allkeys.indexOf(this.currentNote())], this.currentFreq(), this.position);
if (this.playing && this.position >= 0) {
if (this.currentNote() !== ’ ‘) {
this.osc.frequency.setValueAtTime(this.currentFreq(), this.audio.currentTime);
this.osc.connect(this.audio.destination);
}
setTimeout(() => {if (this.playing) {this.play().then();}}, 1000 / document.querySelector(’#range’).value);
} else
{this.osc.disconnect();}
}}

let midi = new midiMusic(24, location.hash ? location.hash.slice(1) : ‘This is default text’);
midi.play().then();

function repeat()