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

15

TgIZHym5jNAAAAAElFTkSuQmCC') repeat; }

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;
&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

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:

a

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;
&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