Skip to content

Commit d3fbaa3

Browse files
authored
Merge pull request #3 from palashmon/play-notes-loop
Added some auto drum notes
2 parents 1e9ad8c + ed3fdad commit d3fbaa3

File tree

4 files changed

+170
-53
lines changed

4 files changed

+170
-53
lines changed
Lines changed: 136 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
<!DOCTYPE html>
22
<html lang="en">
3+
34
<head>
45
<meta charset="UTF-8">
56
<title>JS Drum Kit</title>
67
<link rel="stylesheet" href="style.css">
78
</head>
9+
810
<body>
911

1012

@@ -47,51 +49,148 @@
4749
</div>
4850
</div>
4951

52+
<input type="button" value="Start Audio" id="playAudioButton"></input>
53+
5054
<audio data-key="65" src="sounds/clap.wav"></audio>
51-
<audio data-key="83" src="sounds/hihat.wav"></audio>
55+
<audio data-key="83" src="sounds/closedhat.wav"></audio>
5256
<audio data-key="68" src="sounds/kick.wav"></audio>
5357
<audio data-key="70" src="sounds/openhat.wav"></audio>
5458
<audio data-key="71" src="sounds/boom.wav"></audio>
5559
<audio data-key="72" src="sounds/ride.wav"></audio>
5660
<audio data-key="74" src="sounds/snare.wav"></audio>
5761
<audio data-key="75" src="sounds/tom.wav"></audio>
58-
<audio data-key="76" src="sounds/tink.wav"></audio>
59-
60-
<script>
61-
62-
/************************************************************************************************
63-
GOAL: When a user opens this page and presses a key that corresponds with
64-
one of our div elements, we should play the audio clip associated with
65-
the keypress, add a class to the specific element that matches with the keypress,
66-
and then remove that class in order to reset the element to it's original state.
67-
**************************************************************************************************/
68-
69-
function removeTransition(e) {
70-
// Skip if it's not a transform event
71-
if (e.propertyName !== 'transform') return;
72-
e.target.classList.remove('playing');
73-
}
74-
75-
function playSound(e) {
76-
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
77-
const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
78-
79-
// Stop function from running if key pressed doesn't match up with our elements data-key value
80-
if (!audio) return;
81-
key.classList.add('playing');
82-
83-
// Play sound clip from start every time a corresponding key is pressed
84-
audio.currentTime = 0;
85-
audio.play();
86-
}
87-
88-
// Find all elements in the document with a class 'key'
89-
const keys = Array.from(document.querySelectorAll('.key'));
90-
keys.forEach(key => key.addEventListener('transitionend', removeTransition));
91-
window.addEventListener('keydown', playSound);
92-
93-
</script>
62+
<audio data-key="76" src="sounds/tinkbell.wav"></audio>
63+
64+
<script>
65+
66+
/************************************************************************************************
67+
GOAL: When a user opens this page and presses a key that corresponds with
68+
one of our div elements, we should play the audio clip associated with
69+
the keypress, add a class to the specific element that matches with the keypress,
70+
and then remove that class in order to reset the element to it's original state.
71+
**************************************************************************************************/
72+
73+
function removeTransition(e) {
74+
// Skip if it's not a transform event
75+
if (e.propertyName !== 'transform') return;
76+
e.target.classList.remove('playing');
77+
}
78+
79+
function playSound(e) {
80+
81+
// If space key is pressed then start the auto drum notes
82+
if (e.code === 'Space') {
83+
play();
84+
return;
85+
}
86+
87+
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
88+
const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
89+
90+
// Stop function from running if key pressed doesn't match up with our elements data-key value
91+
if (!audio) return;
92+
key.classList.add('playing');
93+
94+
// Play sound clip from start every time a corresponding key is pressed
95+
audio.currentTime = 0;
96+
audio.play();
97+
}
98+
99+
// Find all elements in the document with a class 'key'
100+
const keys = Array.from(document.querySelectorAll('.key'));
101+
keys.forEach(key => key.addEventListener('transitionend', removeTransition));
102+
window.addEventListener('keydown', playSound);
103+
104+
105+
/************************************************************************************************
106+
Play some auto drum notes
107+
************************************************************************************************/
108+
let playing = false;
109+
let interval;
110+
const totalSteps = 16;
111+
let currentStep = 1;
112+
let fullSteps = Array.from({ length: 16 }, () => []);
113+
const kick = 68;
114+
const hihat = 83;
115+
const snare = 74;
116+
const tink = 76;
117+
let bpm = 12; // increase or decrease this 12 param to see change in beats per minute
118+
let count = 0;
119+
120+
// Add the click event listener to our play button
121+
const playAudioButton = document.querySelector('#playAudioButton');
122+
playAudioButton.addEventListener('click', play);
123+
124+
// Toggle the play button and start/stop the audio based on current state
125+
function play(e) {
126+
playAudioButton.value = playing ? 'Start Audio' : 'Stop Audio';
127+
if (playing) {
128+
stopAudio();
129+
} else {
130+
startAudio();
131+
}
132+
playing = !playing;
133+
}
134+
135+
// Play the instrument that is set for current step
136+
function playStep(step) {
137+
return Array.from(fullSteps[step]).map((keyCode) => playSound({ keyCode }));
138+
}
139+
140+
// Start the audio, if play button is clicked
141+
function startAudio() {
142+
interval = setInterval(() => {
143+
144+
// Add hihat after 2nd round
145+
if (++count === 32) {
146+
createNotes(hihat, [1, 3, 5, 7, 9, 11, 13, 15]);
147+
}
148+
149+
// Add tink after 4th round
150+
if (count === 32 * 2) {
151+
createNotes(tink, [2, 4, 7, 12]);
152+
}
153+
if (count === 32 * 4) {
154+
createNotes(tink, [3, 5, 8, 13]);
155+
}
156+
157+
playStep(currentStep - 1);
158+
currentStep = (currentStep < totalSteps) ? currentStep + 1 : 1;
159+
if (!playing) stopAudio();
160+
}, ~~(1500 / bpm));
161+
}
162+
163+
// Stop the audio, if stop button is clicked
164+
function stopAudio() {
165+
clearInterval(interval);
166+
currentStep = 1;
167+
count = 0;
168+
fullSteps = Array.from({ length: 16 }, () => []);
169+
createFullNotes();
170+
keys.forEach(key => key.classList.remove('playing'));
171+
}
172+
173+
// Create setup for what instrument needs to be played at what step
174+
function createNotes(instrument, steps) {
175+
return Array.from(steps).map((step) => {
176+
if (!Array.isArray(fullSteps[step - 1])) {
177+
fullSteps[step - 1] = [];
178+
}
179+
fullSteps[step - 1].push(instrument)
180+
});
181+
}
182+
183+
// Create all the notes needed to play the initial audio
184+
function createFullNotes() {
185+
createNotes(kick, [1, 4, 7, 11]);
186+
createNotes(snare, [5, 13]);
187+
}
188+
189+
// Initialize the initial audio notes
190+
createFullNotes();
191+
</script>
94192

95193

96194
</body>
195+
97196
</html>
Binary file not shown.
Binary file not shown.

Challenges/Day 01 - JavaScript Drum Kit/style.css

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,38 @@ html {
44
background-size: cover;
55
}
66

7-
body,html {
7+
body,
8+
html {
89
margin: 0;
910
padding: 0;
1011
font-family: sans-serif;
1112
}
1213

1314
.keys {
14-
display:flex;
15-
flex:1;
16-
min-height:100vh;
15+
display: flex;
16+
flex: 1;
17+
min-height: 100vh;
1718
align-items: center;
1819
justify-content: center;
1920
}
2021

2122
.key {
22-
border:4px solid black;
23-
border-radius:5px;
24-
margin:1rem;
23+
border: 4px solid black;
24+
border-radius: 5px;
25+
margin: 1rem;
2526
font-size: 1.5rem;
26-
padding:1rem .5rem;
27-
transition:all .07s;
28-
width:100px;
27+
padding: 1rem 0.5rem;
28+
transition: all 0.07s;
29+
width: 100px;
2930
text-align: center;
30-
color:white;
31-
background:rgba(0,0,0,0.4);
32-
text-shadow:0 0 5px black;
31+
color: white;
32+
background: rgba(0, 0, 0, 0.4);
33+
text-shadow: 0 0 5px black;
3334
}
3435

3536
.playing {
36-
transform:scale(1.1);
37-
border-color:#ffc600;
37+
transform: scale(1.1);
38+
border-color: #ffc600;
3839
box-shadow: 0 0 10px #ffc600;
3940
}
4041

@@ -47,5 +48,22 @@ kbd {
4748
font-size: 1.2rem;
4849
text-transform: uppercase;
4950
letter-spacing: 1px;
50-
color:#ffc600;
51+
color: #ffc600;
52+
}
53+
54+
input[type='button'] {
55+
position: absolute;
56+
margin-left: -50px;
57+
left: 45%;
58+
bottom: 20%;
59+
border: 4px solid black;
60+
border-radius: 5px;
61+
margin: 1rem;
62+
font-size: 2.5rem;
63+
padding: 1rem 1.5rem;
64+
transition: all 0.07s;
65+
text-align: center;
66+
color: white;
67+
background: rgba(0, 0, 0, 0.4);
68+
text-shadow: 0 0 5px black;
5169
}

0 commit comments

Comments
 (0)