mirror of
https://github.com/TrentSPalmer/fcc-challenges.git
synced 2024-11-21 11:21:30 -08:00
add drum-machine
This commit is contained in:
parent
5ef5e70118
commit
997ef13aaa
466
drum-machine/drumMachine.css
Normal file
466
drum-machine/drumMachine.css
Normal file
@ -0,0 +1,466 @@
|
||||
body {
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tool-tip-class {
|
||||
background-color: #756d58;
|
||||
font-size: 2rem;
|
||||
font-style: oblique;
|
||||
border-color: black !important;
|
||||
border-width: 2px !important;
|
||||
border-style: solid;
|
||||
border-radius: 6px;
|
||||
width: unset !important;
|
||||
max-width: unset !important;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#test {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#drum-machine {
|
||||
background-color: #756d58;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#display {
|
||||
background-color: #586075;
|
||||
margin: auto;
|
||||
height: 70vh;
|
||||
min-height: 400px;
|
||||
width: 80vw;
|
||||
border-radius: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#display-top, #display-bottom {
|
||||
flex-basis: 19%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
text-align: center;
|
||||
font-size: x-large;
|
||||
font-style: oblique;
|
||||
}
|
||||
|
||||
#display-middle {
|
||||
flex-basis: 62%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-ms-user-select: none; /* IE 10+ and Edge */
|
||||
user-select: none; /* Standard syntax */
|
||||
}
|
||||
|
||||
#display-middle-left {
|
||||
flex-basis: 55%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#display-middle-left-b {
|
||||
flex-basis: 90%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
#drum-pad-grid {
|
||||
margin: auto;
|
||||
background-color: #607558;
|
||||
border-radius: 1rem;
|
||||
width: 54vh;
|
||||
height: 45vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
padding: 0 0 2vh 2vh;
|
||||
}
|
||||
|
||||
.drum-pad-row {
|
||||
height: 33%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.drum-pad {
|
||||
border-radius: 1rem;
|
||||
width: 33%;
|
||||
background-color: #6d5875;
|
||||
border: black;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
margin: 2vh 2vh 0 0;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
font-size: x-large;
|
||||
font-style: oblique;
|
||||
}
|
||||
|
||||
.drum-pad > p {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.selectionMenu {
|
||||
margin: auto;
|
||||
background-color: #607558;
|
||||
border-radius: 1rem;
|
||||
width: 54vh;
|
||||
height: 45vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
scrollbar-color: black #6d5875;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.selectionMenu::-webkit-scrollbar {
|
||||
background-color: #6d5875;
|
||||
}
|
||||
|
||||
.selectionMenu::-webkit-scrollbar-thumb {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.selectionMenuItem, .volumeSelectionMenuItem, .metronomeSelectionMenuItem {
|
||||
min-height: min-content;
|
||||
background-color: #6d5875;
|
||||
margin: 10px 20px 20px 10px;
|
||||
padding: 15px;
|
||||
border-radius: 1rem;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
text-align: center;
|
||||
font-style: bold;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
#display-middle-right {
|
||||
flex-basis: 45%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#display-middle-left-a, #display-middle-left-c {
|
||||
flex-basis: 5%;
|
||||
}
|
||||
|
||||
#display-middle-right-b, #display-middle-right-c, #display-middle-right-d {
|
||||
flex-basis: 12%;
|
||||
}
|
||||
|
||||
#display-middle-right-c {
|
||||
min-width: 15px;
|
||||
}
|
||||
|
||||
#display-middle-right-b, #display-middle-right-d {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
#display-middle-right-e, #display-middle-right-g {
|
||||
flex-basis: 5%;
|
||||
}
|
||||
|
||||
#display-middle-right-e {
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
#display-middle-right-f {
|
||||
flex-basis: 20%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.selection {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 15vh;
|
||||
min-width: 110px;
|
||||
height: 12vh;
|
||||
min-height: 80px;
|
||||
background-color: #6d5875;
|
||||
border-radius: 1rem;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.selection > p {
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
#volume-container {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
height: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
#volume {
|
||||
margin-top: 13px;
|
||||
margin-bottom: 13px;
|
||||
height: 205px;
|
||||
width: 4px;
|
||||
background: #333333;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#volume > .ui-slider-range {
|
||||
background: #333333;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#volume > .ui-slider-handle {
|
||||
border: none;
|
||||
margin-left: -2px;
|
||||
margin-bottom: -15px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 30px;
|
||||
background: #333333;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
i.fa-volume-down {
|
||||
color: #333333;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
i.fa-volume-up {
|
||||
color: #333333;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.attachment-full {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
height: 149px;
|
||||
width: 149px;
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 1520px) {
|
||||
#display {
|
||||
width: 90vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 1258px) {
|
||||
#display {
|
||||
width: 99vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: portrait) {
|
||||
#display {
|
||||
margin: auto;
|
||||
height: 90vh;
|
||||
width: 95vw;
|
||||
min-width: unset;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
#display-top {
|
||||
flex-basis: 12%;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
#display-bottom {
|
||||
flex-basis: 8%;
|
||||
}
|
||||
|
||||
#display-middle {
|
||||
flex-basis: 80%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
#display-middle-left {
|
||||
flex-basis: unset;
|
||||
}
|
||||
|
||||
#display-middle-right {
|
||||
flex-basis: unset;
|
||||
justify-content: space-around;
|
||||
width: 80%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#select-volume, #display-middle-right-f, #display-middle-right-g {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#display-middle-right-a, #display-middle-right-c, #display-middle-right-e {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#display-middle-right-b, #display-middle-right-d {
|
||||
flex-basis: unset;
|
||||
min-width: unset;
|
||||
}
|
||||
|
||||
#display-middle-right-b, #display-middle-right-d {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.selection {
|
||||
max-height: 80px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#drum-pad-grid {
|
||||
min-width: unset;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
#drum-pad-grid, .selectionMenu {
|
||||
width: 72vw;
|
||||
height: 60vw;
|
||||
}
|
||||
|
||||
.selectionMenuItem, .volumeSelectionMenuItem, .metronomeSelectionMenuItem {
|
||||
padding: unset;
|
||||
}
|
||||
|
||||
.attachment-full {
|
||||
height: 99px;
|
||||
width: 99px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-device-width: 767px) {
|
||||
#drum-pad-grid, .selectionMenu {
|
||||
width: 84vw;
|
||||
height: 70vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-device-width: 750px) {
|
||||
#display-top {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
#drum-pad-grid, .selectionMenu {
|
||||
width: 72vw;
|
||||
height: 60vw;
|
||||
}
|
||||
|
||||
.selection {
|
||||
max-height: 40px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#display-middle {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-device-width: 1024px) {
|
||||
#display {
|
||||
height: 90vh;
|
||||
width: 90vw;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
#display-middle-left {
|
||||
flex-basis: 65%;
|
||||
}
|
||||
|
||||
#display-middle-right {
|
||||
flex-basis: 35%;
|
||||
justify-content: space-around;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#select-volume, #display-middle-right-f, #display-middle-right-g {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#display-middle-right-a, #display-middle-right-c, #display-middle-right-e {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#display-middle-right-b, #display-middle-right-d {
|
||||
flex-basis: unset;
|
||||
min-width: unset;
|
||||
}
|
||||
|
||||
#drum-pad-grid {
|
||||
min-width: unset;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
#drum-pad-grid, .selectionMenu {
|
||||
width: 72vh;
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.selectionMenuItem, .volumeSelectionMenuItem, .metronomeSelectionMenuItem {
|
||||
padding: unset;
|
||||
}
|
||||
}
|
||||
@media (orientation: landscape) and (max-device-height: 767px) {
|
||||
#drum-pad-grid {
|
||||
min-width: unset;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
#drum-pad-grid, .selectionMenu {
|
||||
width: 84vh;
|
||||
height: 70vh;
|
||||
}
|
||||
|
||||
#display-middle-left {
|
||||
flex-basis: 60%;
|
||||
}
|
||||
|
||||
#display-middle-right {
|
||||
flex-basis: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-height: 400px) {
|
||||
#display-top {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.attachment-full {
|
||||
height: 99px;
|
||||
width: 99px;
|
||||
}
|
||||
}
|
221
drum-machine/drumMachine.js
Normal file
221
drum-machine/drumMachine.js
Normal file
@ -0,0 +1,221 @@
|
||||
$(document).ready(function () {
|
||||
|
||||
initialSamples();
|
||||
|
||||
let volume = sessionStorage.hasOwnProperty("volume") ?
|
||||
parseInt(sessionStorage.getItem('volume')) : 20;
|
||||
|
||||
$('#volume').slider({
|
||||
orientation: "vertical",
|
||||
min: 0,
|
||||
max: 100,
|
||||
value: volume,
|
||||
range:"max",
|
||||
slide: function(event,ui) {
|
||||
sessionStorage.setItem('volume',ui.value);
|
||||
makeVolumeToolTip('#display-middle-right-f');
|
||||
}
|
||||
});
|
||||
|
||||
$(document).keydown(function(e){
|
||||
if (qweasdzxc.includes(e.keyCode) && (!anyMenuDisplayed())) {
|
||||
playSample(String.fromCharCode(e.keyCode)+'pad');
|
||||
} else if (e.keyCode === 27 && menuDisplayed) {
|
||||
makeSelection('( cancel -- back )');
|
||||
} else if (e.keyCode === 27 && volumeMenuDisplayed) {
|
||||
makeVolumeSelection('( cancel -- back )');
|
||||
} else if (e.keyCode === 27 && metronomeMenuDisplayed) {
|
||||
makeMetronomeSelection('( cancel -- back )');
|
||||
} else if ([37,38,39,40].includes(e.keyCode)) {
|
||||
}
|
||||
});
|
||||
|
||||
padsArray.forEach(pad => {
|
||||
makeAudioControl(pad);
|
||||
});
|
||||
|
||||
makeVolumeToolTip('#display-middle-right-f');
|
||||
|
||||
$('#selection').on('click',function() {
|
||||
if (!anyMenuDisplayed()) {
|
||||
makeSelection("keys");
|
||||
}
|
||||
});
|
||||
|
||||
$('#display-middle').on('click','.selectionMenuItem',function() {
|
||||
makeSelection($(this).text());
|
||||
});
|
||||
|
||||
$('#select-volume').on('click',function() {
|
||||
if (!anyMenuDisplayed()) {
|
||||
makeVolumeSelection("keys");
|
||||
}
|
||||
});
|
||||
|
||||
$('#display-middle').on('click','.volumeSelectionMenuItem',function() {
|
||||
makeVolumeSelection($(this).text());
|
||||
});
|
||||
|
||||
$('#metronome').on('click',function() {
|
||||
if (!anyMenuDisplayed()) {
|
||||
makeMetronomeSelection("keys");
|
||||
}
|
||||
});
|
||||
|
||||
$('#display-middle').on('click','.metronomeSelectionMenuItem',function() {
|
||||
makeMetronomeSelection($(this).text());
|
||||
});
|
||||
|
||||
$('#reset').on('click',function() {
|
||||
if (!anyMenuDisplayed()) {
|
||||
reset();
|
||||
}
|
||||
});
|
||||
|
||||
$('#stop').on('click',function() {
|
||||
stopMetronomes();
|
||||
});
|
||||
|
||||
padsArray.forEach(pad => {
|
||||
makeMetronomeIcons(pad);
|
||||
});
|
||||
});
|
||||
|
||||
const makeMetronomeIcons = (pad) => {
|
||||
const html = $('#'+pad+'pad').html();
|
||||
if (sessionStorage.getItem(pad + 'isMetronome') === 'true') {
|
||||
$('#'+pad+'pad').html('<p>' + html + ' <i class="fa fa-refresh"></i></p>');
|
||||
}
|
||||
};
|
||||
|
||||
const makeVolumeToolTip = (element) => {
|
||||
$(element).tooltip({
|
||||
content: sessionStorage.getItem('volume'),
|
||||
show: {delay: 250, duration: 0},
|
||||
classes: {
|
||||
"ui-tooltip": "highlight"
|
||||
},
|
||||
tooltipClass: "tool-tip-class",
|
||||
});
|
||||
};
|
||||
|
||||
const makeAudioControl = (pad) => {
|
||||
const audioControl = pad + '<audio class="clip" id="'+pad+'" src="'+sampleUrlPreFix+sessionStorage.getItem(pad)+'"></audio>';
|
||||
$('#'+pad+'pad').html(audioControl);
|
||||
};
|
||||
|
||||
const sampleUrlPreFix = "https://trentpalmer.org/drumsamples/"
|
||||
const qweasdzxc = [81,87,69,65,83,68,90,88,67];
|
||||
|
||||
const playSample = (key) => {
|
||||
const audioFileText = $('#'+key[0]).attr('src').slice(36)
|
||||
$('#display-top').html(key[0] +": " + audioFileText.replace(/\//g,' ').replace(/-/g,'‑'));
|
||||
if (sessionStorage.getItem(key[0] + 'isMetronome') === 'false') {
|
||||
const keyDuration = 1000;
|
||||
const sound = document.getElementById(key[0]);
|
||||
const audioFile = sessionStorage.getItem(key[0]);
|
||||
const thisVolume = getVolume(key[0]);
|
||||
if (sound.currentTime === 0) {
|
||||
sound.volume = thisVolume;
|
||||
sound.play();
|
||||
setTimeout(function(){
|
||||
const soundDuration = Math.floor(sound.duration);
|
||||
if (soundDuration > (keyDuration / 1000)) {
|
||||
sound.currentTime = soundDuration;
|
||||
}
|
||||
}, keyDuration);
|
||||
} else {
|
||||
const newSound = new Audio(sampleUrlPreFix + audioFile);
|
||||
newSound.volume = thisVolume;
|
||||
newSound.play();
|
||||
setTimeout(function(){
|
||||
const newSoundDuration = Math.floor(newSound.duration);
|
||||
if (newSoundDuration > (keyDuration / 1000)) {
|
||||
newSound.currentTime = newSoundDuration;
|
||||
}
|
||||
}, keyDuration);
|
||||
}
|
||||
} else {
|
||||
metronome(key[0]);
|
||||
sessionStorage.setItem(key[0]+'metronomeIsPlaying',
|
||||
(sessionStorage.getItem(key[0]+'metronomeIsPlaying')
|
||||
=== 'false' ? 'true' : 'false'));
|
||||
}
|
||||
};
|
||||
|
||||
const metronome = (key) => {
|
||||
let keyDuration = parseBPM(key);
|
||||
const sound = document.getElementById(key);
|
||||
sound.volume = getVolume(key);
|
||||
sound.play();
|
||||
let refreshMetronome = setInterval(function(){
|
||||
keyDuration = parseBPM(key);
|
||||
sound.pause();
|
||||
sound.currentTime = 0;
|
||||
sound.volume = getVolume(key);
|
||||
sound.play();
|
||||
if (sessionStorage.getItem(key + 'metronomeIsPlaying') === 'false') {
|
||||
clearInterval(refreshMetronome);
|
||||
}
|
||||
},keyDuration);
|
||||
};
|
||||
|
||||
const parseBPM = (key) => {
|
||||
const bpm = parseInt(sessionStorage
|
||||
.getItem(key+'metronomeTempo').slice(-10).split(' ')[1]);
|
||||
return Math.round(60000 / bpm);
|
||||
};
|
||||
|
||||
const getVolume = (key) => {
|
||||
const machineVolume = parseInt(sessionStorage.getItem('volume'));
|
||||
const padVolumeOffset = parseInt(sessionStorage.getItem(key+'volume'));
|
||||
const thisVolume = (machineVolume + padVolumeOffset) / 100;
|
||||
return thisVolume > 1 ? 1 : thisVolume < 0 ? 0 : thisVolume;
|
||||
};
|
||||
|
||||
const initialSamples = () => {
|
||||
if (!sessionStorage.hasOwnProperty("volume")) {
|
||||
sessionStorage.setItem("volume","30");
|
||||
}
|
||||
padsArray.forEach(pad => {
|
||||
if (!sessionStorage.hasOwnProperty(pad + "volume")) {
|
||||
sessionStorage.setItem(pad + "volume","+0");
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty(pad + "isMetronome")) {
|
||||
sessionStorage.setItem(pad + "isMetronome",false);
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty(pad + "metronomeIsPlaying")) {
|
||||
sessionStorage.setItem(pad + "metronomeIsPlaying",false);
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty(pad + "metronomeTempo")) {
|
||||
sessionStorage.setItem(pad + "metronomeTempo","Andante: 92 bpm");
|
||||
}
|
||||
});
|
||||
if (!sessionStorage.hasOwnProperty("Q")) {
|
||||
sessionStorage.setItem("Q","Assorted-Hits/Cymbals/CYCdh_Crash-01.wav");
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty("W")) {
|
||||
sessionStorage.setItem("W","Assorted-Hits/Cymbals/CYCdh_MultiCrash-01.wav");
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty("E")) {
|
||||
sessionStorage.setItem("E","Assorted-Hits/Cymbals/CYCdh_MultiCrashHi-01.wav");
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty("A")) {
|
||||
sessionStorage.setItem("A","Assorted-Hits/Cymbals/CYCdh_MultiCrashLo-01.wav");
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty("S")) {
|
||||
sessionStorage.setItem("S","Assorted-Hits/Snares/Ludwig-A/CYCdh_LudFlamA-05.wav");
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty("D")) {
|
||||
sessionStorage.setItem("D","Assorted-Hits/Snares/Ludwig-A/CYCdh_LudRimA-07.wav");
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty("Z")) {
|
||||
sessionStorage.setItem("Z","Assorted-Hits/Kicks/Loose-Kick/CYCdh_LooseKick-08.wav");
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty("X")) {
|
||||
sessionStorage.setItem("X","Assorted-Hits/Snares/Ludwig-A/CYCdh_LudSnrA-05.wav");
|
||||
}
|
||||
if (!sessionStorage.hasOwnProperty("C")) {
|
||||
sessionStorage.setItem("C","Assorted-Hits/Snares/Ludwig-A/CYCdh_LudSnrOffA-08.wav");
|
||||
}
|
||||
}
|
111
drum-machine/index.html
Normal file
111
drum-machine/index.html
Normal file
@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="#" />
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
|
||||
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
|
||||
<link rel="stylesheet" type="text/css" href="drumMachine.css">
|
||||
<title>Drum Machine - Build A Drum Machine - Front End Libraries Projects</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="test">
|
||||
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
|
||||
</div>
|
||||
<div id="drum-machine">
|
||||
<a href="https://github.com/TrentSPalmer/fcc-challenges/tree/gh-pages/drum-machine">
|
||||
<img src="https://github.blog/wp-content/uploads/2008/12/forkme_right_white_ffffff.png?resize=149%2C149"
|
||||
class="attachment-full size-full" alt="Fork me on GitHub" data-recalc-dims="1">
|
||||
</a>
|
||||
<div id="display">
|
||||
<div id="display-top"></div>
|
||||
<div id="display-middle">
|
||||
<div id="display-middle-left">
|
||||
<div id="display-middle-left-a"></div>
|
||||
<div id="display-middle-left-b">
|
||||
<div id="drum-pad-grid">
|
||||
<div class="drum-pad-row">
|
||||
<div class="drum-pad" id="Qpad" title="" onclick="playSample($(this).attr('id'))">
|
||||
<p>Q</p>
|
||||
</div>
|
||||
<div class="drum-pad" id="Wpad" title="" onclick="playSample($(this).attr('id'))">
|
||||
<p>W</p>
|
||||
</div>
|
||||
<div class="drum-pad" id="Epad" title="" onclick="playSample($(this).attr('id'))">
|
||||
<p>E</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drum-pad-row">
|
||||
<div class="drum-pad" id="Apad" title="" onclick="playSample($(this).attr('id'))">
|
||||
<p>A</p>
|
||||
</div>
|
||||
<div class="drum-pad" id="Spad" title="" onclick="playSample($(this).attr('id'))">
|
||||
<p>S</p>
|
||||
</div>
|
||||
<div class="drum-pad" id="Dpad" title="" onclick="playSample($(this).attr('id'))">
|
||||
<p>D</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drum-pad-row">
|
||||
<div class="drum-pad" id="Zpad" title="" onclick="playSample($(this).attr('id'))">
|
||||
<p>Z</p>
|
||||
</div>
|
||||
<div class="drum-pad" id="Xpad" title="" onclick="playSample($(this).attr('id'))">
|
||||
<p>X</p>
|
||||
</div>
|
||||
<div class="drum-pad" id="Cpad" title="" onclick="playSample($(this).attr('id'))">
|
||||
<p>C</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="display-middle-left-c"></div>
|
||||
</div>
|
||||
<div id="display-middle-right">
|
||||
<div id="display-middle-right-a"></div>
|
||||
<div id="display-middle-right-b">
|
||||
<div id="selection" class="selection" title=''><p>select<br/>a sound</p>
|
||||
</div>
|
||||
<div id="select-volume" class="selection" title=''><p>select<br/>a volume</p>
|
||||
</div>
|
||||
<div id="metronome" class="selection" title=''><p>metronome</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="display-middle-right-c">
|
||||
</div>
|
||||
<div id="display-middle-right-d">
|
||||
<div id="reset" class="selection" title=''><p>RESET</p>
|
||||
</div>
|
||||
<div id="stop" class="selection" title=''><p>STOP</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="display-middle-right-e" title="">
|
||||
</div>
|
||||
<div id="display-middle-right-f" title="">
|
||||
<div id="volume-container">
|
||||
<i class="fa fa-volume-up"></i>
|
||||
<div id="volume"></div>
|
||||
<i class="fa fa-volume-down"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div id="display-middle-right-g" title="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="display-bottom"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="metronomeTempos.js"></script>
|
||||
<script type="text/javascript" src="makeSelection.js"></script>
|
||||
<script type="text/javascript" src="wavFiles.js"></script>
|
||||
<script type="text/javascript" src="drumMachine.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
291
drum-machine/makeSelection.js
Normal file
291
drum-machine/makeSelection.js
Normal file
@ -0,0 +1,291 @@
|
||||
let drumPadGrid = {};
|
||||
let fileStringArray = [];
|
||||
let padSelectingFor = '';
|
||||
let menuDisplayed = false;
|
||||
let volumeMenuDisplayed = false;
|
||||
let metronomeMenuDisplayed = false;
|
||||
|
||||
const anyMenuDisplayed = () => {
|
||||
return menuDisplayed || volumeMenuDisplayed || metronomeMenuDisplayed;
|
||||
};
|
||||
|
||||
const padsArray = ['Q','W','E','A','S','D','Z','X','C'];
|
||||
const volumeMenuArray = ['+30','+20','+10','0','-10','-20','-30'];
|
||||
|
||||
const stopMetronomes = () => {
|
||||
padsArray.forEach(pad => {
|
||||
sessionStorage.setItem(pad + 'metronomeIsPlaying', 'false');
|
||||
});
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
sessionStorage.clear();
|
||||
initialSamples();
|
||||
padsArray.forEach(pad => {
|
||||
makeAudioControl(pad);
|
||||
});
|
||||
};
|
||||
|
||||
const makeMetronomeSelection = (selection) => {
|
||||
if (selection === "keys") {
|
||||
padSelectingFor = '';
|
||||
drumPadGrid = document.getElementById('drum-pad-grid');
|
||||
metronomeMenuDisplayed = true;
|
||||
showKeyMetronomeSelectionMenu();
|
||||
} else if (padsArray.includes(selection)) {
|
||||
padSelectingFor = selection;
|
||||
showMetronomeMenu();
|
||||
} else if (selection === 'Metronome Off') {
|
||||
sessionStorage.setItem(padSelectingFor + 'isMetronome',false);
|
||||
metronomeMenuDisplayed = false;
|
||||
$('#display-middle-left-b').html((drumPadGrid));
|
||||
makeAudioControl(padSelectingFor);
|
||||
} else if (selection.includes('bpm')) {
|
||||
sessionStorage.setItem(padSelectingFor + 'metronomeTempo',selection);
|
||||
sessionStorage.setItem(padSelectingFor + 'isMetronome',true);
|
||||
metronomeMenuDisplayed = false;
|
||||
$('#display-middle-left-b').html((drumPadGrid));
|
||||
makeAudioControl(padSelectingFor);
|
||||
makeMetronomeIcons(padSelectingFor);
|
||||
} else if (selection === '( cancel -- back )') {
|
||||
metronomeMenuDisplayed = false;
|
||||
$('#display-middle-left-b').html((drumPadGrid));
|
||||
}
|
||||
};
|
||||
|
||||
const showMetronomeMenu = () => {
|
||||
const selectionMenu = Object.keys(metronomeTempos)
|
||||
.map(item => (makeMetronomeSelectionMenuItem(item + ": " + metronomeTempos[item])));
|
||||
|
||||
selectionMenu.unshift(makeMetronomeSelectionMenuItem('Metronome Off'));
|
||||
showMetronomeSelectionMenuItems(selectionMenu);
|
||||
};
|
||||
|
||||
const showKeyMetronomeSelectionMenu = () => {
|
||||
const metronomeSelectionMenu = padsArray
|
||||
.map(item => (makeMetronomeSelectionMenuItem(item)));
|
||||
|
||||
showMetronomeSelectionMenuItems(metronomeSelectionMenu);
|
||||
};
|
||||
|
||||
const makeMetronomeSelectionMenuItem = (selector) => {
|
||||
return '<div id="foo" class="metronomeSelectionMenuItem" title="">' + selector + '</div>';
|
||||
};
|
||||
|
||||
const showMetronomeSelectionMenuItems = (menuItems) => {
|
||||
menuItems.unshift(makeMetronomeSelectionMenuItem('( cancel -- back )'));
|
||||
$('#display-middle-left-b').html(('<div id="metronomeSelectionMenu" class="selectionMenu"></div>'));
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let j = i + 3000;
|
||||
$('#metronomeSelectionMenu').append(menuItems[i].replace('foo',j));
|
||||
$('#'+j).tooltip({
|
||||
content: makeMetronomeSelectionMenuItemToolTipContent($('#'+j).text()),
|
||||
position: { my: "right top+150", at: "center bottom" },
|
||||
show: {delay: 250, duration: 0},
|
||||
classes: {
|
||||
"ui-tooltip": "highlight"
|
||||
},
|
||||
tooltipClass: "tool-tip-class",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const makeMetronomeSelectionMenuItemToolTipContent = (text) => {
|
||||
if (padsArray.includes(text)) {
|
||||
return sessionStorage.getItem(text + 'isMetronome') === "false" ? 'Metronome Off' : sessionStorage.getItem(text + 'metronomeTempo');
|
||||
} else if (text === '( cancel -- back )') {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
const makeVolumeSelection = (selection) => {
|
||||
if (selection === "keys") {
|
||||
padSelectingFor = '';
|
||||
drumPadGrid = document.getElementById('drum-pad-grid');
|
||||
volumeMenuDisplayed = true;
|
||||
showKeyVolumeSelectionMenu();
|
||||
} else if (padsArray.includes(selection)) {
|
||||
padSelectingFor = selection;
|
||||
showVolumeMenu();
|
||||
} else if (volumeMenuArray.includes(selection)) {
|
||||
sessionStorage.setItem((padSelectingFor + 'volume'),selection);
|
||||
volumeMenuDisplayed = false;
|
||||
$('#display-middle-left-b').html((drumPadGrid));
|
||||
} else if (selection === '( cancel -- back )') {
|
||||
volumeMenuDisplayed = false;
|
||||
$('#display-middle-left-b').html((drumPadGrid));
|
||||
}
|
||||
};
|
||||
|
||||
const showVolumeMenu = () => {
|
||||
const selectionMenu = volumeMenuArray
|
||||
.map(item => (makeVolumeSelectionMenuItem(item)));
|
||||
|
||||
showVolumeSelectionMenuItems(selectionMenu);
|
||||
};
|
||||
|
||||
const showKeyVolumeSelectionMenu = () => {
|
||||
const volumeSelectionMenu = padsArray
|
||||
.map(item => (makeVolumeSelectionMenuItem(item)));
|
||||
|
||||
showVolumeSelectionMenuItems(volumeSelectionMenu);
|
||||
};
|
||||
|
||||
const makeVolumeSelectionMenuItem = (selector) => {
|
||||
return '<div id="foo" class="volumeSelectionMenuItem" title="">' + selector + '</div>';
|
||||
};
|
||||
|
||||
const showVolumeSelectionMenuItems = (menuItems) => {
|
||||
menuItems.unshift(makeVolumeSelectionMenuItem('( cancel -- back )'));
|
||||
$('#display-middle-left-b').html(('<div id="volumeSelectionMenu" class="selectionMenu"></div>'));
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let j = i + 2000;
|
||||
$('#volumeSelectionMenu').append(menuItems[i].replace('foo',j));
|
||||
$('#'+j).tooltip({
|
||||
content: makeVolumeSelectionMenuItemToolTipContent($('#'+j).text()),
|
||||
position: { my: "right top+150", at: "center bottom" },
|
||||
show: {delay: 250, duration: 0},
|
||||
classes: {
|
||||
"ui-tooltip": "highlight"
|
||||
},
|
||||
tooltipClass: "tool-tip-class",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const makeVolumeSelectionMenuItemToolTipContent = (text) => {
|
||||
if (padsArray.includes(text)) {
|
||||
return 'volume offset for ' + text;
|
||||
} else if (text === '( cancel -- back )') {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
const makeSelection = (selection) => {
|
||||
|
||||
if (selection === "keys") {
|
||||
padSelectingFor = '';
|
||||
fileStringArray = [];
|
||||
drumPadGrid = document.getElementById('drum-pad-grid');
|
||||
menuDisplayed = true;
|
||||
showKeySelectionMenu();
|
||||
} else if (padsArray.includes(selection)) {
|
||||
padSelectingFor = selection;
|
||||
showFirstDirMenu();
|
||||
} else if (selection.substring(selection.length - 4) !== '.wav' && selection !== '( cancel -- back )') {
|
||||
fileStringArray.push(selection);
|
||||
if (fileStringArray.length === 1) {
|
||||
showSecondDirMenu();
|
||||
} else if (fileStringArray.length === 2) {
|
||||
showThirdDirMenu();
|
||||
} else {
|
||||
showFourthDirMenu();
|
||||
}
|
||||
} else if (selection.substring(selection.length - 4) === '.wav') {
|
||||
fileStringArray.push(selection);
|
||||
sessionStorage.setItem(padSelectingFor,fileStringArray.join('/'));
|
||||
menuDisplayed = false;
|
||||
$('#display-middle-left-b').html((drumPadGrid));
|
||||
makeAudioControl(padSelectingFor);
|
||||
} else if (selection === '( cancel -- back )') {
|
||||
if (fileStringArray.length === 0) {
|
||||
if (padSelectingFor === '') {
|
||||
menuDisplayed = false;
|
||||
$('#display-middle-left-b').html((drumPadGrid));
|
||||
} else {
|
||||
padSelectingFor = '';
|
||||
showKeySelectionMenu();
|
||||
}
|
||||
} else if (fileStringArray.length === 1) {
|
||||
fileStringArray = [];
|
||||
showFirstDirMenu();
|
||||
} else if (fileStringArray.length === 2) {
|
||||
fileStringArray.pop();
|
||||
showSecondDirMenu();
|
||||
} else if (fileStringArray.length === 3) {
|
||||
fileStringArray.pop();
|
||||
showThirdDirMenu();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const showFourthDirMenu = () => {
|
||||
const selectionMenu = wavFiles[fileStringArray[0]][fileStringArray[1]][0][fileStringArray[2]]
|
||||
.map(item => (makeSelectionMenuItem(item)));
|
||||
showSelectionMenuItems(selectionMenu);
|
||||
};
|
||||
|
||||
|
||||
const showThirdDirMenu = () => {
|
||||
const selectionMenu = wavFiles[fileStringArray[0]][fileStringArray[1]]
|
||||
.filter(item => typeof item === 'string')
|
||||
.map(item => (makeSelectionMenuItem(item)));
|
||||
|
||||
if (!(typeof wavFiles[fileStringArray[0]][fileStringArray[1]][0] === 'string')) {
|
||||
const moreDirs = Object.keys(wavFiles[fileStringArray[0]][fileStringArray[1]][0])
|
||||
.map(item => (makeSelectionMenuItem(item)));
|
||||
moreDirs.forEach(item => selectionMenu.push(item));
|
||||
}
|
||||
showSelectionMenuItems(selectionMenu);
|
||||
};
|
||||
|
||||
|
||||
const showSecondDirMenu = () => {
|
||||
const selectionMenu = Object.keys(wavFiles[fileStringArray[0]])
|
||||
.map(item => (makeSelectionMenuItem(item)));
|
||||
|
||||
showSelectionMenuItems(selectionMenu);
|
||||
};
|
||||
|
||||
|
||||
const showFirstDirMenu = () => {
|
||||
const selectionMenu = Object.keys(wavFiles)
|
||||
.map(item => (makeSelectionMenuItem(item)));
|
||||
|
||||
showSelectionMenuItems(selectionMenu);
|
||||
};
|
||||
|
||||
|
||||
const showKeySelectionMenu = () => {
|
||||
const selectionMenu = padsArray
|
||||
.map(item => (makeSelectionMenuItem(item)));
|
||||
|
||||
showSelectionMenuItems(selectionMenu);
|
||||
};
|
||||
|
||||
const showSelectionMenuItems = (menuItems) => {
|
||||
menuItems.unshift(makeSelectionMenuItem('( cancel -- back )'));
|
||||
$('#display-middle-left-b').html(('<div id="selectionMenu" class="selectionMenu"></div>'));
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let j = i + 1000;
|
||||
$('#selectionMenu').append(menuItems[i].replace('foo',j));
|
||||
$('#'+j).tooltip({
|
||||
content: makeSelectionMenuItemToolTipContent($('#'+j).text()),
|
||||
position: { my: "right top+150", at: "center bottom" },
|
||||
show: {delay: 250, duration: 0},
|
||||
classes: {
|
||||
"ui-tooltip": "highlight"
|
||||
},
|
||||
tooltipClass: "tool-tip-class",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const makeSelectionMenuItemToolTipContent = (text) => {
|
||||
if (padsArray.includes(text)) {
|
||||
return 'select for ' + text;
|
||||
} else if (text === '( cancel -- back )') {
|
||||
return text;
|
||||
} else if (fileStringArray.length === 0) {
|
||||
return 'select for ' + padSelectingFor + ': ' + text;
|
||||
} else if (fileStringArray.length === 1) {
|
||||
return 'select for ' + padSelectingFor + ': ' + fileStringArray[0] + '/' + text;
|
||||
} else {
|
||||
return 'select for ' + padSelectingFor + ': ' + fileStringArray.join('/') + '/' + text;
|
||||
}
|
||||
}
|
||||
|
||||
const makeSelectionMenuItem = (selector) => {
|
||||
return '<div id="foo" class="selectionMenuItem" title="">' + selector + '</div>';
|
||||
};
|
24
drum-machine/metronomeTempos.js
Normal file
24
drum-machine/metronomeTempos.js
Normal file
@ -0,0 +1,24 @@
|
||||
const metronomeTempos = {
|
||||
'Larghissimo': '20 bpm',
|
||||
'Adagissimo': '24 bpm',
|
||||
'Grave': '35 bpm',
|
||||
'Largo': '50 bpm',
|
||||
'Lento': '53 bpm',
|
||||
'Larghetto': '63 bpm',
|
||||
'Adagio': '71 bpm',
|
||||
'Adagietto': '75 bpm',
|
||||
'Marcia moderato': '84 bpm',
|
||||
'Andante': '92 bpm',
|
||||
'Andantino': '94 bpm',
|
||||
'Andante moderato': '95 bpm',
|
||||
'Moderato': '105 bpm',
|
||||
'Allegretto': '106 bpm',
|
||||
'Allegro moderato': '118 bpm',
|
||||
'Allegro': '138 bpm',
|
||||
'Vivace': '166 bpm',
|
||||
'Vivacissimo': '174 bpm',
|
||||
'Allegrissimo': '174 bpm',
|
||||
'Allegro vivace': '174 bpm',
|
||||
'Presto': '184 bpm',
|
||||
'Prestissimo': '205 bpm',
|
||||
};
|
1038
drum-machine/wavFiles.js
Normal file
1038
drum-machine/wavFiles.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user