Sie sind auf Seite 1von 6

1 <!

DOCTYPE html>
2 <head>
3 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
4 <title>Musical Keyboard - JS Dynamic Audio Synth</title>
5 <meta name="description" content="A keyboard! Play with it. ;) A synthesizer created
entirely in HTML5 + JS that dynamically generates modulated Waveform Audio dataURIs."
/>
6 <meta name="keywords" content="keith, keith horwood, keithwhor, social media, uwo,
westernu, start-up, corporation, CEO, science, technology, blog, youtube, dataURI,
javascript, JS, java, HTML5, piano, keyboard, HTML5 piano, HTML5 keyboard, waveform
audio, .wav, .wav audio, audio" />
7 <meta name="author" content="Keith William Horwood" />
8 <meta name="robots" content="index,follow,archive" />
9 <meta property="og:description" content="A keyboard! Play with it. ;) A synthesizer
created entirely in HTML5 + JS that dynamically generates modulated Waveform Audio
dataURIs." />
10 <meta property="og:image" content="http://keithwhor.com/music/opengraph.png" />
11 <style>
12
13 body { font-family: Helvetica; color: rgb(32,32,32); padding: 10px;
14
background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADg
zO9IAAAAM0lEQVQIW2P8DwQMMPDwIAODvD2YxwiXQBJESKAJQiQeHPgP0w43EsUoZFGgCQg70BwAAN
TgIZHym5jNAAAAAElFTkSuQmCC') repeat;
15 }
16
17 b { font-weight: bold; }
18
19 .key { position: absolute; font-family: Helvetica; font-weight: 100; font-size:
12px;
20 border: 1px solid rgba(32,32,32,0.2);
21 border-radius: 0px 0px 5px 5px;
22 cursor:pointer;
23 box-shadow: 0px 5px 1px rgba(32,32,32,0.2);
24 -webkit-transition: margin 0.05s ease, background-color 0.05s ease,
box-shadow 0.05s ease; }
25 .key:hover { background-color: rgb(255,192,32); }
26
27 .key .label { position: absolute; bottom: 5px; text-align: center; left: 0px;
right: 0px; }
28
29 .black { background-color: rgb(32,32,32); color: #ffffff; z-index: 1;
text-shadow: 0px -1px 1px rgba(255,255,255,0.5); }
30
31 .white { background-color: #ffffff; color: rgb(32,32,32); z-index: 0;
text-shadow: 0px 1px 1px rgba(32,32,32,0.5); }
32
33 .title { text-shadow: 0px 1px 1px rgba(32,32,32,0.2); font-size: 40px;
font-weight: bold; font-family: Helvetica; padding: 10px; text-align: center; }
34
35 .sub { color: rgb(96,96,96); font-size: 30px; padding: 10px; font-weight:100;
margin:10px 0px; text-shadow: 0px 1px 1px rgba(32,32,32,0.2); text-align:center; }
36 .sub a, .sub a:link, .sub a:visited, .sub a:active { font-weight:bold; color:
rgb(128,160,255); text-decoration: none; }
37 .sub a:hover { color: rgb(160,192,255); }
38
39 .source a { color: rgb(255,96,96); }
40 .source a:link, .source a:visited, .source a:active { color: rgb(255,96,96); }
41 .source a:hover { color: rgb(255,128,128); }
42
43 .small { font-size: 20px; }
44
45 .keyboard-options { margin: 30px auto; width: auto; text-align: center;
font-size: 12px; font-weight: 200; padding:10px; }
46
47 .keyboard-holder { margin: 30px auto; height: 200px; position:relative;
user-select:none;
-webkit-user-select:none;-moz-user-select:none;-o-user-select:none; }
48
49 .about { position: relative; max-width: 700px; margin: 30px auto; }
50 .about .header { background-color:rgba(32,64,128,0.5); border-radius: 10px 10px
0px 0px; color:rgb(255,255,255); text-shadow:0px 1px 0px rgb(96,96,96);
51 position: relative; max-width: 600px; margin: 0 auto;
52 font-size: 30px; font-weight: bold; padding: 20px; text-align:center; }
53 .about .contents { font-size: 16px; line-height: 20px; background-color:
rgb(255,255,255); font-weight: 200; padding: 20px; text-align: left; position:
relative;
54 color: rgb(32,32,32); text-shadow: 0px 1px 0px rgb(224,224,224);
55 box-shadow: 0px 5px 10px rgba(32,32,32,0.5); -webkit-box-shadow: 0px 5px
10px rgba(32,32,32,0.5); border: 1px solid rgb(192,192,192); }
56 .about .footer { background-color:rgba(32,64,128,0.5); border-radius: 0px 0px
10px 10px; color:rgb(255,255,255);
57 position: relative; max-width: 600px; margin: 0 auto; font-weight: bold;
padding: 20px; }
58
59 .about a, .about a:link, .about a:visited, .about a:active { font-weight:bold;
color: rgb(224,96,32); text-decoration: none; }
60 .about a:hover { color: rgb(224,128,64); }
61
62 .code { border: 1px solid rgba(32,32,32,0.2); color: rgb(32,32,32); font-family:
Courier New, Courier, monospace; font-size: 12px; white-space:pre; padding:
10px; margin: 10px; }
63
64 .image { border: 1px solid rgba(32,32,32,0.2); color: rgb(32,32,32);
font-family: Courier New, Courier, monospace; font-size: 12px; white-space:pre;
padding: 10px; margin: 10px; text-align: center; }
65
66 </style>
67
68 <script src="audiosynth.js"></script>
69 <script src="audiosynth.view.js"></script>
70
71 </head>
72
73 <body>
74 <a href="https://github.com/keithwhor"><img style="position: fixed; top: 0; right:
0; border: 0; z-index: 99" src="
https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on
GitHub"></a>
75 <div class="title">Musical Keyboard - JS Dynamic Audio Synth</div>
76 <div class="sub">Created by <a href="http://keithwhor.com">Keith William Horwood</a>
&copy; 2013<br />
77 <div class="small"><br><div class="source"><a href="
http://keithwhor.github.io/audiosynth">[ Source Code and Usage ]</a></div><br>
78 <a href="http://twitter.com/keithwhor">[ Twitter ]</a></div></div>
79 <div class="keyboard-options">
80 Sound
81 <select ID="sound">
82 <option value="0" selected>Keyboard</option>
83 <option value="1">Organ</option>
84 <option value="2">Acoustic Guitar</option>
85 <option value="3">EDM, bro!</option>
86 </select>
87 <div ID="keyboard" class="keyboard-holder"></div>
88 <div class="keyboard-options">
89 Range [C<span ID="OCTAVE_LOWER">3</span>-B<span ID="OCTAVE_UPPER">5</span>]
90 <input type="button" ID="-_OCTAVE" value="-" />
91 <input type="button" ID="+_OCTAVE" value="+" /><br />
92 <i>(Use left/right arrows to adjust with keyboard)</i>
93 </div>
94 <div class="about">
95 <div class="header">What is this?</div>
96 <div class="contents">
97 This is a an emulated keyboard (a synthesizer!) that spans three musical octaves
(C3-B5).<br /><br />
98 Give it a shot, click any key with your mouse, or use the keys on your keyboard
as indicated on the screen!<br /><br />
99 (If you don't hear anything, try using <a href="http://google.com/chrome">Google
Chrome</a> or update your browser to the newest version. Mobile devices aren't
all quite supported yet.)
100 </div>
101 <div class="footer"></div>
102 </div>
103 <div class="about">
104 <div class="header">How does it work?</div>
105 <div class="contents">
106 This keyboard works by generating <a href="http://en.wikipedia.org/wiki/.WAV_file
">Waveform Audio</a> file data dynamically, converting it into a <a href="
http://en.wikipedia.org/wiki/Data_URI_scheme">base64-encoded dataURI</a>, and
subsequently playing it using the <a href="
http://www.w3.org/wiki/HTML/Elements/audio">HTML5 audio element</a> from within
your web browser.<br /><br />
107 Basically, we're using math to make sounds. Hooray. This is different than other
HTML5 pianos in that it doesn't <a href="http://html5piano.com/">use
pre-recorded audio files</a>, and that it's a <a href="http://mrcoles.com/piano/"
>little bit more refined</a>.
108 </div>
109 <div class="footer"></div>
110 </div>
111 <div class="about">
112 <div class="header">The Theory</div>
113 <div class="contents">
114 So you really want to get into details? Well, first of all, creating waveform is
simple enough (it's just a sine wave, and we have a <i>Math.sin()</i> function
in JS!), and all of the information you need about packing Waveform Audio data
and creating the dataURI can be found <a href="
http://www.sk89q.com/playground/jswav/">here, with much thanks to sk89q</a>. I'm
going to assume a basic understanding of Javascript in this brief walkthrough,
don't hesitate to <a href="http://keithwhor.com/">e-mail me</a> if you have any
questions or concerns!<br /><br />
115 Unfortunately, all you get out of creating a sine wave is an extremely bland
(and to be blunt, annoying) flat tone.<br /><br />
116 Real musical instruments create full, lively notes. We want to emulate that. How
do we go about doing so?<br /><br />
117 Well, thank the University of Salford - Manchester for <a href="
http://www.acoustics.salford.ac.uk/acoustics_info/sound_synthesis/">this lovely
page</a>. I'll provide the less mathematically-inclined with a bit of a walk
through, so you can visualize what we're doing at each step.<br /><br />
118
119 <b>Step 1. Understanding your sine wave.</b><br /><br />
120
121 Alright, so you copy-and-pasted the code from sk89q's page above and you have
your sine wave based on the <a href="http://www.phy.mtu.edu/~suits/notefreqs.html
">frequency of note you want to produce</a>. Actually, what you have is a bunch
of text that programatically generates the data points for a sine wave. Mamma mia!
122 <div class="code">var v = volume * Math.sin((2 * Math.PI) * (i / sampleRate) *
frequency);</div>
123 So, first of all, if you're unfamiliar with waveforms I'll give you a brief
overview of what we'd like to do here, along with how we're going to do it. You
need to understand the following equation:
124 <div class="code">FREQUENCY (F) = 1 / PERIOD (T)</div>
125 Your period is the amount of time (t) it takes for one full oscillation of your
wave. The frequency, measured in Hz (the unit 1/s) is the amount of oscillations
your wave performs per second. I'm sure most of you took some sort of physics
class, so I'll avoid lecturing, but basically every sound is merely just a
"buzz", or vibration in the air. The faster this buzz (higher frequency, more
oscillations per second, shorter period), the higher the pitch of the note.<br />
<br />
126 From the code I've outlined above, we end up with a waveform that looks like
this (thanks to <a href="http://en.wikipedia.org/wiki/Sine_wave">Wikipedia</a>
for this image):
127 <div class="image"><img src="sinewave.png" style="width:400px;" /></div>
128 One period is every two sets of vertical dotted lines. This wave is what's
making our flat, ugly tone, and we want to change it. Imagine this continuing on
a couple of hundred times, fluctuating between <i>-volume</i> and <i>+volume</i>
(the top and bottom horizontal dotted lines, i.e., the amplitude of the wave).<br
/><br />
129 So what can we do?<br /><br />
130
131 <b>Step 2. Transforming your sine wave.</b><br /><br />
132
133 We can now use the wonderful world of <i>maths (omg!)</i> to help us out. We
want to transform our sine function, as defined above, into something a little
more... real. Something you'd hear in the natural world. So, how does a real
instrument create sound? (<a href="
http://www.acoustics.salford.ac.uk/acoustics_info/sound_synthesis/">Image Credit
</a>.)
134 <div class="image"><img src="instrument_create.jpg" /></div>
135 Well, we have a few stages. During the <i>attack</i>, you're plucking/hitting a
string (in the case of a piano) or initializing the oscillation which will
produce your sound. There's a bit of a decay, then a hold, should you decide to
maintain to oscillation, and finally a release (and subsequent death of the
signal). Now, let's forget about the hold and release right now, and simplify
our keyboard into just an <i>attack</i> and subsequent <i>decay</i>. Basically,
what we want to do is start off with the <i>Amplitude of our sine wave at 0%</i>
, bring it to a <i>peak of 100%</i> in a very short amount of time (the attack
on a piano is extremely fast!) and then decay afterwards. We accomplish this
with what I call a <i>dampener</i>.<br /><br />
136 First, the <i>attack</i>:
137 <div class="code">var attack = 0.002;
138 if(i<=sampleRate*attack) {
139 // Linear build-up, fast.
140 curVol = volume * (i/(sampleRate*attack));
141 }</div>
142 What we're doing here is setting our attack time to 0.002s (2ms!) and linearly
increasing the volume (wave amplitude) from the start of the wave until we reach
the end of the attack (again, the 2ms). <i>i</i> is our counter variable,
indicating our current position in the waveform. Though this might be barely
perceptible to your average person, increasing the attack time to even 0.02s
(20ms) has serious consequences on your sound.<br /><br />
143 Next, the <i>decay</i>:
144 <div class="code">else {
145 // Decay. Exponentially increasing (faster) decay
146 // at higher frequencies due to logarithmic dampener.
147 var dampen = Math.pow(0.5*Math.log((frequency*volume)/sampleRate),2);
148 curVol = volume * Math.pow(
149 (1-((i-(sampleRate*attack))/(sampleRate*(seconds-attack)))),dampen
150 );
151 }</div>
152 Woo! That's a lot of stuff. Logarithms and exponents oh my! What we're doing
here is first setting a <i>dampener</i>, which is just a mathematical function
that creates a larger number at higher frequencies. You'll notice that if you
hit a low note on your piano, it sticks around for a lot longer than a high
note. I want to emulate that, and this is a "hack" to approximate it without
actually simulating artificial strings and doing <i>real physics</i>. The last
line at the bottom? Just saying we want to decay toward 0, in a polynomial
fashion based on the value of our dampener.<br /><br />
153 Please note that <i>curVol</i> now contains our modified (dampened) <i>Amplitude
</i>. We will apply it to the wave with everything else in the next step.<br />
<br />
154 The reason we use a logarithm is that note frequencies scale exponentially (each
note is 2x the frequency of the one below it in octave) and we don't quite want
a doubling.<br /> I got the best sounding results using this algorithm after
fiddling around for a while. Here are two others you can play with:
155 <div class="code">var dampen2 = Math.log((frequency*volume)/sampleRate);
156 var dampen3 = 1 + (0.01 * frequency);</div>
157 <br /><br />
158
159 <b>Step 3. Adding noise.</b><br /><br />
160
161 Alright. We've created the basis for modifying our waveform to give it some <i>
life</i>. However, it's still going to sound extremely, extremely robotic. So
now we'll do two things. We're first going to add noise to our signal, and then
we're going to modulate it back to the carrier signal to normalize it back to
within the range we'd like.
162
163 <div class="code">// This can generate noise by creating out-of-phase waves.
164 var base = function(x) {
165 x = x || 0;
166 return Math.sin((2 * Math.PI) * (i / sampleRate) * frequency + (x * Math.PI));
167 };
168
169 var mod = [];
170
171 mod.push(function(x) {
172 return 1 * Math.sin(2 * Math.PI * ((i / sampleRate) * frequency) + x); });
173 mod.push(function(x) {
174 return 0.5 * Math.sin(2 * Math.PI * ((i / sampleRate) * frequency) + x); });
175 mod.push(function(x) {
176 return 0.25 * Math.sin(2 * Math.PI * ((i / sampleRate) * frequency) + x); });
177
178 v = mod[0](Math.pow(base(0), 2) + (0.75 * base(0.25)) + (0.1 * base(0.5)));
179
180 v = Math.min(Math.max(v, -1), 1);
181
182 v = curVol * v;</div>
183
184 What we have here is <i>base()</i>, our function which generates our wave. If we
don't give it a value, it returns our basic waveform. If we do give it a value,
it shifts the wave by <i>0.5*x</i> periods. Note that any multiple of 2 here
will give an overlapping period and will just give us our initial waveform. The
idea is that you can create an out-of-phase wave with the same frequency that
will interfere with your basic waveform and "warp" it, removing the "perfect",
or flat tone from it.<br /><br />
185 The <i>mod[]</i> array contains frequency modulation functions, you can use them
however you'd wish, but always apply <i>mod[0]</i> at the end - it contains your
carrier wave, and acts as a way to normalize all of the interference you create
by combining and modulating other waveforms.<br /><br />
186 Our final waveform is a modulated combination of our initial waveform
interfering with two "quieter" versions of itself, one at 75% amplitude, and the
other at 10% amplitude, both slightly out-of-phase with their parent.<br /><br />
187 Finally, we multiply <i>curVol</i>, our dampened amplitude, by our final
waveform to finish creating our note.<br /><br />
188
189 <b>Step 4. Output</b><br /><br />
190
191 Take your data, base64 encode it, set the source data of an HTML5 Audio object
to your newly generated dataURI and play it! Voila, a musical note. Hopefully
you get the basics of what we're doing to our waveforms here. :)<br /><br />
192
193 <b>Addendum 1. Other instruments, and Karplus-Strong string synthesis</b><br />
<br />
194
195 So, since initially creating this page, I've implemented a number of different
instruments. The organ is much like the piano, although with a more extended
attack. The "EDM" is just a highly-modulated version of the piano with a bit of
harmonic resonance added in. The <i>acoustic guitar</i>, however, is an entirely
different ball game.<br /><br />
196 The "plucked string" sound is generated by what's called <a href="
http://en.wikipedia.org/wiki/Karplus%E2%80%93Strong_string_synthesis">
Karplus-Strong string synthesis</a>. In a nutshell, you create a buffer that's
the size of your period (based on your sampling rate and frequency). This buffer
holds noise, it basically just alternates (randomly) between an amplitude of +1
and -1. You output this buffer repeatedly to give your note its pitch due to its
periodicity. However, each additional time you loop through the "noise" buffer,
you average each point in the buffer with the one immediately preceding it,
effectively filtering it. What this causes is a decay of your signal over time,
and it "smooths" out your initially chaotic waveform. You can read more about
the <a href="http://music.columbia.edu/cmc/musicandcomputers/chapter4/04_09.php">
Karplus-Strong algorithm at Columbia Faculty of Music</a>, or on <a href="
http://dev.hasenj.org/post/4517734448">Hasen el Judy's blog</a>.</div>
197 <div class="footer"></div>
198 </div>
199 <div class="about">
200 <div class="header">Thanks!</div>
201 <div class="contents">
202 Thanks for visiting, hope you enjoyed!<br /><br />
203 Special thanks to <a href="http://dev.hasenj.org/post/4517734448">Hasen el Judy
</a> for tips on using the <a href="
http://en.wikipedia.org/wiki/Karplus%E2%80%93Strong_string_synthesis">
Karplus-Strong string synthesis</a> algorithm to emulate plucked string
instruments!
204 </div>
205 <div class="footer"></div>
206 </div>
207 <div class="about">
208 <div class="header">Disclaimer</div>
209 <div class="contents">
210 All code here, unless noted otherwise, is &copy; 2013 <a href="
http://keithwhor.com/">Keith William Horwood</a> with use of modified scripting
from <a href="http://www.sk89q.com/playground/jswav/">Dynamic .WAV generation</a>
. Feel free to use and subsequently modify it as you see fit, but please give
credit where credit is due! Thanks! :)
211 </div>
212 <div class="footer"></div>
213 </div>
214
215 <script type="text/javascript">
216
217 var a = new AudioSynthView();
218 a.draw();
219
220 </script>
221 <script>
222 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
223 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
224 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
225 })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
226
227 ga('create', 'UA-44899638-2', 'keithwhor.com');
228 ga('send', 'pageview');
229
230 </script>
231 </body>
232 </html>
233

Das könnte Ihnen auch gefallen