earl 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1471 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://0xfe.blogspot.com/
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Content-Type:
22
+ - text/html; charset=UTF-8
23
+ Expires:
24
+ - Tue, 29 Jul 2025 09:54:37 GMT
25
+ Date:
26
+ - Tue, 29 Jul 2025 09:54:37 GMT
27
+ Cache-Control:
28
+ - private, max-age=0
29
+ Last-Modified:
30
+ - Mon, 09 Jun 2025 12:31:35 GMT
31
+ Etag:
32
+ - W/"d9ecb14bc27900564451bb6132fca8a617847311bf76c3f6e60ee6d70df2730c"
33
+ X-Content-Type-Options:
34
+ - nosniff
35
+ X-Xss-Protection:
36
+ - 1; mode=block
37
+ Server:
38
+ - GSE
39
+ Alt-Svc:
40
+ - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
41
+ Transfer-Encoding:
42
+ - chunked
43
+ body:
44
+ encoding: ASCII-8BIT
45
+ string: "<!DOCTYPE html>\n<html class='v2' dir='ltr' xmlns='http://www.w3.org/1999/xhtml'
46
+ xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data'
47
+ xmlns:expr='http://www.google.com/2005/gml/expr'>\n<head>\n<link href='https://www.blogger.com/static/v1/widgets/573632073-css_bundle_v2.css'
48
+ rel='stylesheet' type='text/css'/>\n<!--[if lte IE 8]> <meta content='IE=EmulateIE7'
49
+ http-equiv='X-UA-Compatible'/> <![endif]-->\n<meta content='width=1100' name='viewport'/>\n<meta
50
+ content='text/html; charset=UTF-8' http-equiv='Content-Type'/>\n<meta content='blogger'
51
+ name='generator'/>\n<link href='https://0xfe.blogspot.com/favicon.ico' rel='icon'
52
+ type='image/x-icon'/>\n<link href='http://0xfe.blogspot.com/' rel='canonical'/>\n<link
53
+ rel=\"alternate\" type=\"application/atom+xml\" title=\"0xFE - 11111110b -
54
+ 0376 - Atom\" href=\"https://0xfe.blogspot.com/feeds/posts/default\" />\n<link
55
+ rel=\"alternate\" type=\"application/rss+xml\" title=\"0xFE - 11111110b -
56
+ 0376 - RSS\" href=\"https://0xfe.blogspot.com/feeds/posts/default?alt=rss\"
57
+ />\n<link rel=\"service.post\" type=\"application/atom+xml\" title=\"0xFE
58
+ - 11111110b - 0376 - Atom\" href=\"https://draft.blogger.com/feeds/19544619/posts/default\"
59
+ />\n<link rel=\"me\" href=\"https://draft.blogger.com/profile/11179501091623983192\"
60
+ />\n<!--Can't find substitution for tag [blog.ieCssRetrofitLinks]-->\n<meta
61
+ content='http://0xfe.blogspot.com/' property='og:url'/>\n<meta content='0xFE
62
+ - 11111110b - 0376' property='og:title'/>\n<meta content='' property='og:description'/>\n<title>0xFE
63
+ - 11111110b - 0376</title>\n<style type='text/css'>@font-face{font-family:'Cardo';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/cardo/v20/wlp_gwjKBV1pqhvP3IE7225PUCk.woff2)format('woff2');unicode-range:U+0304-0305,U+0308,U+0331,U+10330-1034A;}@font-face{font-family:'Cardo';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/cardo/v20/wlp_gwjKBV1pqhv03IE7225PUCk.woff2)format('woff2');unicode-range:U+1F00-1FFF;}@font-face{font-family:'Cardo';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/cardo/v20/wlp_gwjKBV1pqhv73IE7225PUCk.woff2)format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF;}@font-face{font-family:'Cardo';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/cardo/v20/wlp_gwjKBV1pqhv63IE7225PUCk.woff2)format('woff2');unicode-range:U+0307-0308,U+0590-05FF,U+200C-2010,U+20AA,U+25CC,U+FB1D-FB4F;}@font-face{font-family:'Cardo';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/cardo/v20/wlp_gwjKBV1pqhu63IE7225PUCk.woff2)format('woff2');unicode-range:U+10300-1032F;}@font-face{font-family:'Cardo';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/cardo/v20/wlp_gwjKBV1pqhvM3IE7225PUCk.woff2)format('woff2');unicode-range:U+16A0-16F8;}@font-face{font-family:'Cardo';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/cardo/v20/wlp_gwjKBV1pqhv23IE7225PUCk.woff2)format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}@font-face{font-family:'Cardo';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/cardo/v20/wlp_gwjKBV1pqhv43IE7225P.woff2)format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}</style>\n<style
64
+ id='page-skin-1' type='text/css'><!--\n/*\n-----------------------------------------------\nBlogger
65
+ Template Style\nName: Simple\nDesigner: Josh Peterson\nURL: www.noaesthetic.com\n-----------------------------------------------
66
+ */\n/* Variable definitions\n====================\n<Variable name=\"keycolor\"
67
+ description=\"Main Color\" type=\"color\" default=\"#66bbdd\"/>\n<Group description=\"Page
68
+ Text\" selector=\"body\">\n<Variable name=\"body.font\" description=\"Font\"
69
+ type=\"font\"\ndefault=\"normal normal 12px Arial, Tahoma, Helvetica, FreeSans,
70
+ sans-serif\"/>\n<Variable name=\"body.text.color\" description=\"Text Color\"
71
+ type=\"color\" default=\"#222222\"/>\n</Group>\n<Group description=\"Backgrounds\"
72
+ selector=\".body-fauxcolumns-outer\">\n<Variable name=\"body.background.color\"
73
+ description=\"Outer Background\" type=\"color\" default=\"#66bbdd\"/>\n<Variable
74
+ name=\"content.background.color\" description=\"Main Background\" type=\"color\"
75
+ default=\"#ffffff\"/>\n<Variable name=\"header.background.color\" description=\"Header
76
+ Background\" type=\"color\" default=\"transparent\"/>\n</Group>\n<Group description=\"Links\"
77
+ selector=\".main-outer\">\n<Variable name=\"link.color\" description=\"Link
78
+ Color\" type=\"color\" default=\"#2288bb\"/>\n<Variable name=\"link.visited.color\"
79
+ description=\"Visited Color\" type=\"color\" default=\"#888888\"/>\n<Variable
80
+ name=\"link.hover.color\" description=\"Hover Color\" type=\"color\" default=\"#33aaff\"/>\n</Group>\n<Group
81
+ description=\"Blog Title\" selector=\".header h1\">\n<Variable name=\"header.font\"
82
+ description=\"Font\" type=\"font\"\ndefault=\"normal normal 60px Arial, Tahoma,
83
+ Helvetica, FreeSans, sans-serif\"/>\n<Variable name=\"header.text.color\"
84
+ description=\"Title Color\" type=\"color\" default=\"#3399bb\" />\n</Group>\n<Group
85
+ description=\"Blog Description\" selector=\".header .description\">\n<Variable
86
+ name=\"description.text.color\" description=\"Description Color\" type=\"color\"\ndefault=\"#777777\"
87
+ />\n</Group>\n<Group description=\"Tabs Text\" selector=\".tabs-inner .widget
88
+ li a\">\n<Variable name=\"tabs.font\" description=\"Font\" type=\"font\"\ndefault=\"normal
89
+ normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif\"/>\n<Variable
90
+ name=\"tabs.text.color\" description=\"Text Color\" type=\"color\" default=\"#999999\"/>\n<Variable
91
+ name=\"tabs.selected.text.color\" description=\"Selected Color\" type=\"color\"
92
+ default=\"#000000\"/>\n</Group>\n<Group description=\"Tabs Background\" selector=\".tabs-outer
93
+ .PageList\">\n<Variable name=\"tabs.background.color\" description=\"Background
94
+ Color\" type=\"color\" default=\"#f5f5f5\"/>\n<Variable name=\"tabs.selected.background.color\"
95
+ description=\"Selected Color\" type=\"color\" default=\"#eeeeee\"/>\n</Group>\n<Group
96
+ description=\"Post Title\" selector=\"h3.post-title, .comments h4\">\n<Variable
97
+ name=\"post.title.font\" description=\"Font\" type=\"font\"\ndefault=\"normal
98
+ normal 22px Arial, Tahoma, Helvetica, FreeSans, sans-serif\"/>\n</Group>\n<Group
99
+ description=\"Date Header\" selector=\".date-header\">\n<Variable name=\"date.header.color\"
100
+ description=\"Text Color\" type=\"color\"\ndefault=\"#000000\"/>\n<Variable
101
+ name=\"date.header.background.color\" description=\"Background Color\" type=\"color\"\ndefault=\"transparent\"/>\n</Group>\n<Group
102
+ description=\"Post Footer\" selector=\".post-footer\">\n<Variable name=\"post.footer.text.color\"
103
+ description=\"Text Color\" type=\"color\" default=\"#666666\"/>\n<Variable
104
+ name=\"post.footer.background.color\" description=\"Background Color\" type=\"color\"\ndefault=\"#f9f9f9\"/>\n<Variable
105
+ name=\"post.footer.border.color\" description=\"Shadow Color\" type=\"color\"
106
+ default=\"#eeeeee\"/>\n</Group>\n<Group description=\"Gadgets\" selector=\"h2\">\n<Variable
107
+ name=\"widget.title.font\" description=\"Title Font\" type=\"font\"\ndefault=\"normal
108
+ bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif\"/>\n<Variable name=\"widget.title.text.color\"
109
+ description=\"Title Color\" type=\"color\" default=\"#000000\"/>\n<Variable
110
+ name=\"widget.alternate.text.color\" description=\"Alternate Color\" type=\"color\"
111
+ default=\"#999999\"/>\n</Group>\n<Group description=\"Images\" selector=\".main-inner\">\n<Variable
112
+ name=\"image.background.color\" description=\"Background Color\" type=\"color\"
113
+ default=\"#ffffff\"/>\n<Variable name=\"image.border.color\" description=\"Border
114
+ Color\" type=\"color\" default=\"#eeeeee\"/>\n<Variable name=\"image.text.color\"
115
+ description=\"Caption Text Color\" type=\"color\" default=\"#000000\"/>\n</Group>\n<Group
116
+ description=\"Accents\" selector=\".content-inner\">\n<Variable name=\"body.rule.color\"
117
+ description=\"Separator Line Color\" type=\"color\" default=\"#eeeeee\"/>\n<Variable
118
+ name=\"tabs.border.color\" description=\"Tabs Border Color\" type=\"color\"
119
+ default=\"transparent\"/>\n</Group>\n<Variable name=\"body.background\" description=\"Body
120
+ Background\" type=\"background\"\ncolor=\"#ffffff\" default=\"$(color) none
121
+ repeat scroll top left\"/>\n<Variable name=\"body.background.override\" description=\"Body
122
+ Background Override\" type=\"string\" default=\"\"/>\n<Variable name=\"body.background.gradient.cap\"
123
+ description=\"Body Gradient Cap\" type=\"url\"\ndefault=\"url(//www.blogblog.com/1kt/simple/gradients_light.png)\"/>\n<Variable
124
+ name=\"body.background.gradient.tile\" description=\"Body Gradient Tile\"
125
+ type=\"url\"\ndefault=\"url(//www.blogblog.com/1kt/simple/body_gradient_tile_light.png)\"/>\n<Variable
126
+ name=\"content.background.color.selector\" description=\"Content Background
127
+ Color Selector\" type=\"string\" default=\".content-inner\"/>\n<Variable name=\"content.padding\"
128
+ description=\"Content Padding\" type=\"length\" default=\"10px\"/>\n<Variable
129
+ name=\"content.padding.horizontal\" description=\"Content Horizontal Padding\"
130
+ type=\"length\" default=\"10px\"/>\n<Variable name=\"content.shadow.spread\"
131
+ description=\"Content Shadow Spread\" type=\"length\" default=\"40px\"/>\n<Variable
132
+ name=\"content.shadow.spread.webkit\" description=\"Content Shadow Spread
133
+ (WebKit)\" type=\"length\" default=\"5px\"/>\n<Variable name=\"content.shadow.spread.ie\"
134
+ description=\"Content Shadow Spread (IE)\" type=\"length\" default=\"10px\"/>\n<Variable
135
+ name=\"main.border.width\" description=\"Main Border Width\" type=\"length\"
136
+ default=\"0\"/>\n<Variable name=\"header.background.gradient\" description=\"Header
137
+ Gradient\" type=\"url\" default=\"none\"/>\n<Variable name=\"header.shadow.offset.left\"
138
+ description=\"Header Shadow Offset Left\" type=\"length\" default=\"-1px\"/>\n<Variable
139
+ name=\"header.shadow.offset.top\" description=\"Header Shadow Offset Top\"
140
+ type=\"length\" default=\"-1px\"/>\n<Variable name=\"header.shadow.spread\"
141
+ description=\"Header Shadow Spread\" type=\"length\" default=\"1px\"/>\n<Variable
142
+ name=\"header.padding\" description=\"Header Padding\" type=\"length\" default=\"30px\"/>\n<Variable
143
+ name=\"header.border.size\" description=\"Header Border Size\" type=\"length\"
144
+ default=\"1px\"/>\n<Variable name=\"header.bottom.border.size\" description=\"Header
145
+ Bottom Border Size\" type=\"length\" default=\"1px\"/>\n<Variable name=\"header.border.horizontalsize\"
146
+ description=\"Header Horizontal Border Size\" type=\"length\" default=\"0\"/>\n<Variable
147
+ name=\"description.text.size\" description=\"Description Text Size\" type=\"string\"
148
+ default=\"140%\"/>\n<Variable name=\"tabs.margin.top\" description=\"Tabs
149
+ Margin Top\" type=\"length\" default=\"0\" />\n<Variable name=\"tabs.margin.side\"
150
+ description=\"Tabs Side Margin\" type=\"length\" default=\"30px\" />\n<Variable
151
+ name=\"tabs.background.gradient\" description=\"Tabs Background Gradient\"
152
+ type=\"url\"\ndefault=\"url(//www.blogblog.com/1kt/simple/gradients_light.png)\"/>\n<Variable
153
+ name=\"tabs.border.width\" description=\"Tabs Border Width\" type=\"length\"
154
+ default=\"1px\"/>\n<Variable name=\"tabs.bevel.border.width\" description=\"Tabs
155
+ Bevel Border Width\" type=\"length\" default=\"1px\"/>\n<Variable name=\"date.header.padding\"
156
+ description=\"Date Header Padding\" type=\"string\" default=\"inherit\"/>\n<Variable
157
+ name=\"date.header.letterspacing\" description=\"Date Header Letter Spacing\"
158
+ type=\"string\" default=\"inherit\"/>\n<Variable name=\"date.header.margin\"
159
+ description=\"Date Header Margin\" type=\"string\" default=\"inherit\"/>\n<Variable
160
+ name=\"post.margin.bottom\" description=\"Post Bottom Margin\" type=\"length\"
161
+ default=\"25px\"/>\n<Variable name=\"image.border.small.size\" description=\"Image
162
+ Border Small Size\" type=\"length\" default=\"2px\"/>\n<Variable name=\"image.border.large.size\"
163
+ description=\"Image Border Large Size\" type=\"length\" default=\"5px\"/>\n<Variable
164
+ name=\"page.width.selector\" description=\"Page Width Selector\" type=\"string\"
165
+ default=\".region-inner\"/>\n<Variable name=\"page.width\" description=\"Page
166
+ Width\" type=\"string\" default=\"auto\"/>\n<Variable name=\"main.section.margin\"
167
+ description=\"Main Section Margin\" type=\"length\" default=\"15px\"/>\n<Variable
168
+ name=\"main.padding\" description=\"Main Padding\" type=\"length\" default=\"15px\"/>\n<Variable
169
+ name=\"main.padding.top\" description=\"Main Padding Top\" type=\"length\"
170
+ default=\"30px\"/>\n<Variable name=\"main.padding.bottom\" description=\"Main
171
+ Padding Bottom\" type=\"length\" default=\"30px\"/>\n<Variable name=\"paging.background\"\ncolor=\"#ffffff\"\ndescription=\"Background
172
+ of blog paging area\" type=\"background\"\ndefault=\"transparent none no-repeat
173
+ scroll top center\"/>\n<Variable name=\"footer.bevel\" description=\"Bevel
174
+ border length of footer\" type=\"length\" default=\"0\"/>\n<Variable name=\"mobile.background.overlay\"
175
+ description=\"Mobile Background Overlay\" type=\"string\"\ndefault=\"transparent
176
+ none repeat scroll top left\"/>\n<Variable name=\"mobile.background.size\"
177
+ description=\"Mobile Background Size\" type=\"string\" default=\"auto\"/>\n<Variable
178
+ name=\"mobile.button.color\" description=\"Mobile Button Color\" type=\"color\"
179
+ default=\"#ffffff\" />\n<Variable name=\"startSide\" description=\"Side where
180
+ text starts in blog language\" type=\"automatic\" default=\"left\"/>\n<Variable
181
+ name=\"endSide\" description=\"Side where text ends in blog language\" type=\"automatic\"
182
+ default=\"right\"/>\n*/\n/* Content\n-----------------------------------------------
183
+ */\nbody, .body-fauxcolumn-outer {\nfont: normal normal 15px Cardo;\ncolor:
184
+ #000000;\nbackground: #ffffff none repeat scroll top left;\npadding: 0 0 0
185
+ 0;\n}\nhtml body .region-inner {\nmin-width: 0;\nmax-width: 100%;\nwidth:
186
+ auto;\n}\na:link {\ntext-decoration:none;\ncolor: #0b5394;\n}\na:visited {\ntext-decoration:none;\ncolor:
187
+ #0b5394;\n}\na:hover {\ntext-decoration:underline;\ncolor: #0b5394;\n}\n.body-fauxcolumn-outer
188
+ .fauxcolumn-inner {\nbackground: transparent none repeat scroll top left;\n_background-image:
189
+ none;\n}\n.body-fauxcolumn-outer .cap-top {\nposition: absolute;\nz-index:
190
+ 1;\nheight: 400px;\nwidth: 100%;\nbackground: #ffffff none repeat scroll top
191
+ left;\n}\n.body-fauxcolumn-outer .cap-top .cap-left {\nwidth: 100%;\nbackground:
192
+ transparent none repeat-x scroll top left;\n_background-image: none;\n}\n.content-outer
193
+ {\n-moz-box-shadow: 0 0 0 rgba(0, 0, 0, .15);\n-webkit-box-shadow: 0 0 0 rgba(0,
194
+ 0, 0, .15);\n-goog-ms-box-shadow: 0 0 0 #333333;\nbox-shadow: 0 0 0 rgba(0,
195
+ 0, 0, .15);\nmargin-bottom: 1px;\n}\n.content-inner {\npadding: 10px 40px;\n}\n.content-inner
196
+ {\nbackground-color: #ffffff;\n}\n/* Header\n-----------------------------------------------
197
+ */\n.header-outer {\nbackground: transparent none repeat-x scroll 0 -400px;\n_background-image:
198
+ none;\n}\n.Header h1 {\nfont: normal normal 42px Cardo;\ncolor: #000000;\ntext-shadow:
199
+ 0 0 0 rgba(0, 0, 0, .2);\n}\n.Header h1 a {\ncolor: #000000;\n}\n.Header .description
200
+ {\nfont-size: 18px;\ncolor: #000000;\n}\n.header-inner .Header .titlewrapper
201
+ {\npadding: 22px 0;\n}\n.header-inner .Header .descriptionwrapper {\npadding:
202
+ 0 0;\n}\n/* Tabs\n----------------------------------------------- */\n.tabs-inner
203
+ .section:first-child {\nborder-top: 0 solid transparent;\n}\n.tabs-inner .section:first-child
204
+ ul {\nmargin-top: -1px;\nborder-top: 1px solid transparent;\nborder-left:
205
+ 1px solid transparent;\nborder-right: 1px solid transparent;\n}\n.tabs-inner
206
+ .widget ul {\nbackground: transparent none repeat-x scroll 0 -800px;\n_background-image:
207
+ none;\nborder-bottom: 1px solid transparent;\nmargin-top: 0;\nmargin-left:
208
+ -30px;\nmargin-right: -30px;\n}\n.tabs-inner .widget li a {\ndisplay: inline-block;\npadding:
209
+ .6em 1em;\nfont: normal normal 15px Cardo;\ncolor: #000000;\nborder-left:
210
+ 1px solid #ffffff;\nborder-right: 1px solid transparent;\n}\n.tabs-inner .widget
211
+ li:first-child a {\nborder-left: none;\n}\n.tabs-inner .widget li.selected
212
+ a, .tabs-inner .widget li a:hover {\ncolor: #000000;\nbackground-color: #eeeeee;\ntext-decoration:
213
+ none;\n}\n/* Columns\n----------------------------------------------- */\n.main-outer
214
+ {\nborder-top: 0 solid transparent;\n}\n.fauxcolumn-left-outer .fauxcolumn-inner
215
+ {\nborder-right: 1px solid transparent;\n}\n.fauxcolumn-right-outer .fauxcolumn-inner
216
+ {\nborder-left: 1px solid transparent;\n}\n/* Headings\n-----------------------------------------------
217
+ */\nh2 {\nmargin: 0 0 1em 0;\nfont: normal normal 11px Cardo;\ncolor: #000000;\ntext-transform:
218
+ uppercase;\n}\n/* Widgets\n-----------------------------------------------
219
+ */\n.widget .zippy {\ncolor: #999999;\ntext-shadow: 2px 2px 1px rgba(0, 0,
220
+ 0, .1);\n}\n.widget .popular-posts ul {\nlist-style: none;\n}\n/* Posts\n-----------------------------------------------
221
+ */\n.date-header span {\nbackground-color: #ffffff;\ncolor: #000000;\npadding:
222
+ 0.4em;\nletter-spacing: 3px;\nmargin: inherit;\n}\n.main-inner {\npadding-top:
223
+ 35px;\npadding-bottom: 65px;\n}\n.main-inner .column-center-inner {\npadding:
224
+ 0 0;\n}\n.main-inner .column-center-inner .section {\nmargin: 0 1em;\n}\n.post
225
+ {\nmargin: 0 0 45px 0;\n}\nh3.post-title, .comments h4 {\nfont: normal normal
226
+ 22px Cardo;\nmargin: .75em 0 0;\n}\n.post-body {\nfont-size: 110%;\nline-height:
227
+ 1.4;\nposition: relative;\n}\n.post-body img, .post-body .tr-caption-container,
228
+ .Profile img, .Image img,\n.BlogList .item-thumbnail img {\npadding: 2px;\nbackground:
229
+ #ffffff;\nborder: 1px solid #dddddd;\n-moz-box-shadow: 1px 1px 5px rgba(0,
230
+ 0, 0, .1);\n-webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1);\nbox-shadow:
231
+ 1px 1px 5px rgba(0, 0, 0, .1);\n}\n.post-body img, .post-body .tr-caption-container
232
+ {\npadding: 5px;\n}\n.post-body .tr-caption-container {\ncolor: #000000;\n}\n.post-body
233
+ .tr-caption-container img {\npadding: 0;\nbackground: transparent;\nborder:
234
+ none;\n-moz-box-shadow: 0 0 0 rgba(0, 0, 0, .1);\n-webkit-box-shadow: 0 0
235
+ 0 rgba(0, 0, 0, .1);\nbox-shadow: 0 0 0 rgba(0, 0, 0, .1);\n}\n.post-header
236
+ {\nmargin: 0 0 1.5em;\nline-height: 1.6;\nfont-size: 90%;\n}\n.post-footer
237
+ {\nmargin: 20px -2px 0;\npadding: 5px 10px;\ncolor: #000000;\nbackground-color:
238
+ #ffffff;\nborder-bottom: 1px solid #eeeeee;\nline-height: 1.6;\nfont-size:
239
+ 90%;\n}\n#comments .comment-author {\npadding-top: 1.5em;\nborder-top: 1px
240
+ solid transparent;\nbackground-position: 0 1.5em;\n}\n#comments .comment-author:first-child
241
+ {\npadding-top: 0;\nborder-top: none;\n}\n.avatar-image-container {\nmargin:
242
+ .2em 0 0;\n}\n#comments .avatar-image-container img {\nborder: 1px solid #dddddd;\n}\n/*
243
+ Comments\n----------------------------------------------- */\n.comments .comments-content
244
+ .icon.blog-author {\nbackground-repeat: no-repeat;\nbackground-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9sLFwMeCjjhcOMAAAD+SURBVDjLtZSvTgNBEIe/WRRnm3U8RC1neQdsm1zSBIU9VVF1FkUguQQsD9ITmD7ECZIJSE4OZo9stoVjC/zc7ky+zH9hXwVwDpTAWWLrgS3QAe8AZgaAJI5zYAmc8r0G4AHYHQKVwII8PZrZFsBFkeRCABYiMh9BRUhnSkPTNCtVXYXURi1FpBDgArj8QU1eVXUzfnjv7yP7kwu1mYrkWlU33vs1QNu2qU8pwN0UpKoqokjWwCztrMuBhEhmh8bD5UDqur75asbcX0BGUB9/HAMB+r32hznJgXy2v0sGLBcyAJ1EK3LFcbo1s91JeLwAbwGYu7TP/3ZGfnXYPgAVNngtqatUNgAAAABJRU5ErkJggg==);\n}\n.comments
245
+ .comments-content .loadmore a {\nborder-top: 1px solid #999999;\nborder-bottom:
246
+ 1px solid #999999;\n}\n.comments .comment-thread.inline-thread {\nbackground-color:
247
+ #ffffff;\n}\n.comments .continue {\nborder-top: 2px solid #999999;\n}\n/*
248
+ Accents\n---------------------------------------------- */\n.section-columns
249
+ td.columns-cell {\nborder-left: 1px solid transparent;\n}\n.blog-pager {\nbackground:
250
+ transparent url(//www.blogblog.com/1kt/simple/paging_dot.png) repeat-x scroll
251
+ top center;\n}\n.blog-pager-older-link, .home-link,\n.blog-pager-newer-link
252
+ {\nbackground-color: #ffffff;\npadding: 5px;\n}\n.footer-outer {\nborder-top:
253
+ 1px dashed #bbbbbb;\n}\n/* Mobile\n-----------------------------------------------
254
+ */\nbody.mobile {\nbackground-size: auto;\n}\n.mobile .body-fauxcolumn-outer
255
+ {\nbackground: transparent none repeat scroll top left;\n}\n.mobile .body-fauxcolumn-outer
256
+ .cap-top {\nbackground-size: 100% auto;\n}\n.mobile .content-outer {\n-webkit-box-shadow:
257
+ 0 0 3px rgba(0, 0, 0, .15);\nbox-shadow: 0 0 3px rgba(0, 0, 0, .15);\npadding:
258
+ 0 0;\n}\nbody.mobile .AdSense {\nmargin: 0 -0;\n}\n.mobile .tabs-inner .widget
259
+ ul {\nmargin-left: 0;\nmargin-right: 0;\n}\n.mobile .post {\nmargin: 0;\n}\n.mobile
260
+ .main-inner .column-center-inner .section {\nmargin: 0;\n}\n.mobile .date-header
261
+ span {\npadding: 0.1em 10px;\nmargin: 0 -10px;\n}\n.mobile h3.post-title {\nmargin:
262
+ 0;\n}\n.mobile .blog-pager {\nbackground: transparent none no-repeat scroll
263
+ top center;\n}\n.mobile .footer-outer {\nborder-top: none;\n}\n.mobile .main-inner,
264
+ .mobile .footer-inner {\nbackground-color: #ffffff;\n}\n.mobile-index-contents
265
+ {\ncolor: #000000;\n}\n.mobile-link-button {\nbackground-color: #0b5394;\n}\n.mobile-link-button
266
+ a:link, .mobile-link-button a:visited {\ncolor: #ffffff;\n}\n.mobile .tabs-inner
267
+ .section:first-child {\nborder-top: none;\n}\n.mobile .tabs-inner .PageList
268
+ .widget-content {\nbackground-color: #eeeeee;\ncolor: #000000;\nborder-top:
269
+ 1px solid transparent;\nborder-bottom: 1px solid transparent;\n}\n.mobile
270
+ .tabs-inner .PageList .widget-content .pagelist-arrow {\nborder-left: 1px
271
+ solid transparent;\n}\n@font-face {\nfont-family: 'Cousine';\nfont-style:
272
+ normal;\nfont-weight: normal;\nsrc: local('Cousine'), url('//themes.googleusercontent.com/static/fonts/cousine/v2/S-JOYqoHekxYm2RHFzWYtgLUuEpTyoUstqEm5AMlJo4.woff')
273
+ format('woff');\n}\ncode {\nfont-family: Cousine;\nfont-size: 14px;\ncolor:
274
+ #333;\n}\n/* Pretty printing styles. Used with prettify.js. */\n.str { color:
275
+ #080; }\n.kwd { color: #008; }\n.com { color: #800; }\n.typ { color: #606;
276
+ }\n.lit { color: #066; }\n.pun { color: #660; }\n.pln { color: #000; }\n.tag
277
+ { color: #008; }\n.atn { color: #606; }\n.atv { color: #080; }\n.dec { color:
278
+ #606; }\npre.prettyprint {\npadding: 6px;\nfont-family: Cousine;\nfont-size:
279
+ 12px;\nbackground: #eee;\n}\n@media print {\n.str { color: #060; }\n.kwd {
280
+ color: #006; font-weight: bold; }\n.com { color: #600; font-style: italic;
281
+ }\n.typ { color: #404; font-weight: bold; }\n.lit { color: #044; }\n.pun {
282
+ color: #440; }\n.pln { color: #000; }\n.tag { color: #006; font-weight: bold;
283
+ }\n.atn { color: #404; }\n.atv { color: #060; }\n}\n#navbar-iframe {\ndisplay:
284
+ none !important;\n}\n/* Links */\na:hover { text-decoration: none; border-bottom:
285
+ dotted #3d85c6 1px; }\n.post-title a:link { color: black; }\n.post-title a:active
286
+ { color: black; }\n.post-title a:visited { color: black; }\n.post-title a:hover
287
+ { color: black; }\n/* Remove line between posts */\ndiv.post-footer { border-bottom:
288
+ white; }\n/* Remove footer */\nfooter { display: none; }\n/* Clean up pager
289
+ */\ndiv.blog-pager { background: white }\ndiv.blog-pager a.home-link { display:
290
+ none }\nspan#blog-pager-older-link { float: left; font-size: 20px; }\ndiv.feed-links
291
+ { display: none; }\n/* Neater looking headers */\nh3 { font-weight: normal;
292
+ }@font-face {\nfont-family: 'Cousine';\nfont-style: normal;\nfont-weight:
293
+ normal;\nsrc: local('Cousine'), url('//themes.googleusercontent.com/static/fonts/cousine/v2/S-JOYqoHekxYm2RHFzWYtgLUuEpTyoUstqEm5AMlJo4.woff')
294
+ format('woff');\n}\ncode {\nfont-family: Cousine;\nfont-size: 14px;\ncolor:
295
+ #333;\n}\n/* Pretty printing styles. Used with prettify.js. */\n.str { color:
296
+ #080; }\n.kwd { color: #008; }\n.com { color: #800; }\n.typ { color: #606;
297
+ }\n.lit { color: #066; }\n.pun { color: #660; }\n.pln { color: #000; }\n.tag
298
+ { color: #008; }\n.atn { color: #606; }\n.atv { color: #080; }\n.dec { color:
299
+ #606; }\npre.prettyprint {\npadding: 6px;\nfont-family: Cousine;\nfont-size:
300
+ 12px;\nbackground: #eee;\n}\n@media print {\n.str { color: #060; }\n.kwd {
301
+ color: #006; font-weight: bold; }\n.com { color: #600; font-style: italic;
302
+ }\n.typ { color: #404; font-weight: bold; }\n.lit { color: #044; }\n.pun {
303
+ color: #440; }\n.pln { color: #000; }\n.tag { color: #006; font-weight: bold;
304
+ }\n.atn { color: #404; }\n.atv { color: #060; }\n}\n#navbar-iframe {\ndisplay:
305
+ none !important;\n}\n/* Links */\na:hover { text-decoration: none; border-bottom:
306
+ dotted #3d85c6 1px; }\n.post-title a:link { color: black; }\n.post-title a:active
307
+ { color: black; }\n.post-title a:visited { color: black; }\n.post-title a:hover
308
+ { color: black; }\n/* Remove line between posts */\ndiv.post-footer { border-bottom:
309
+ white; }\n/* Remove footer */\nfooter { display: none; }\n/* Clean up pager
310
+ */\ndiv.blog-pager { background: white }\ndiv.blog-pager a.home-link { display:
311
+ none }\nspan#blog-pager-older-link { float: left; font-size: 20px; }\ndiv.feed-links
312
+ { display: none; }\n/* Neater looking headers */\nh3 { font-weight: normal;
313
+ }@font-face {\nfont-family: 'Cousine';\nfont-style: normal;\nfont-weight:
314
+ normal;\nsrc: local('Cousine'), url('//themes.googleusercontent.com/static/fonts/cousine/v2/S-JOYqoHekxYm2RHFzWYtgLUuEpTyoUstqEm5AMlJo4.woff')
315
+ format('woff');\n}\ncode {\nfont-family: Cousine;\nfont-size: 14px;\ncolor:
316
+ #333;\n}\n/* Pretty printing styles. Used with prettify.js. */\n.str { color:
317
+ #080; }\n.kwd { color: #008; }\n.com { color: #800; }\n.typ { color: #606;
318
+ }\n.lit { color: #066; }\n.pun { color: #660; }\n.pln { color: #000; }\n.tag
319
+ { color: #008; }\n.atn { color: #606; }\n.atv { color: #080; }\n.dec { color:
320
+ #606; }\npre.prettyprint {\npadding: 6px;\nfont-family: Cousine;\nfont-size:
321
+ 12px;\nbackground: #eee;\n}\n@media print {\n.str { color: #060; }\n.kwd {
322
+ color: #006; font-weight: bold; }\n.com { color: #600; font-style: italic;
323
+ }\n.typ { color: #404; font-weight: bold; }\n.lit { color: #044; }\n.pun {
324
+ color: #440; }\n.pln { color: #000; }\n.tag { color: #006; font-weight: bold;
325
+ }\n.atn { color: #404; }\n.atv { color: #060; }\n}\n#navbar-iframe {\ndisplay:
326
+ none !important;\n}\n/* Links */\na:hover { text-decoration: none; border-bottom:
327
+ dotted #3d85c6 1px; }\n.post-title a:link { color: black; }\n.post-title a:active
328
+ { color: black; }\n.post-title a:visited { color: black; }\n.post-title a:hover
329
+ { color: black; }\n/* Remove line between posts */\ndiv.post-footer { border-bottom:
330
+ white; }\n/* Remove footer */\nfooter { display: none; }\n/* Clean up pager
331
+ */\ndiv.blog-pager { background: white }\ndiv.blog-pager a.home-link { display:
332
+ none }\nspan#blog-pager-older-link { float: left; font-size: 20px; }\ndiv.feed-links
333
+ { display: none; }\n/* Neater looking headers */\nh3 { font-weight: normal;
334
+ }@font-face {\nfont-family: 'Cousine';\nfont-style: normal;\nfont-weight:
335
+ normal;\nsrc: local('Cousine'), url('//themes.googleusercontent.com/static/fonts/cousine/v2/S-JOYqoHekxYm2RHFzWYtgLUuEpTyoUstqEm5AMlJo4.woff')
336
+ format('woff');\n}\ncode {\nfont-family: Cousine;\nfont-size: 14px;\ncolor:
337
+ #333;\n}\n/* Pretty printing styles. Used with prettify.js. */\n.str { color:
338
+ #080; }\n.kwd { color: #008; }\n.com { color: #800; }\n.typ { color: #606;
339
+ }\n.lit { color: #066; }\n.pun { color: #660; }\n.pln { color: #000; }\n.tag
340
+ { color: #008; }\n.atn { color: #606; }\n.atv { color: #080; }\n.dec { color:
341
+ #606; }\npre.prettyprint {\npadding: 6px;\nfont-family: Cousine;\nfont-size:
342
+ 12px;\nbackground: #eee;\n}\n@media print {\n.str { color: #060; }\n.kwd {
343
+ color: #006; font-weight: bold; }\n.com { color: #600; font-style: italic;
344
+ }\n.typ { color: #404; font-weight: bold; }\n.lit { color: #044; }\n.pun {
345
+ color: #440; }\n.pln { color: #000; }\n.tag { color: #006; font-weight: bold;
346
+ }\n.atn { color: #404; }\n.atv { color: #060; }\n}\n#navbar-iframe {\ndisplay:
347
+ none !important;\n}\n/* Links */\na:hover { text-decoration: none; border-bottom:
348
+ dotted #3d85c6 1px; }\n.post-title a:link { color: black; }\n.post-title a:active
349
+ { color: black; }\n.post-title a:visited { color: black; }\n.post-title a:hover
350
+ { color: black; }\n/* Remove line between posts */\ndiv.post-footer { border-bottom:
351
+ white; }\n/* Remove footer */\nfooter { display: none; }\n/* Clean up pager
352
+ */\ndiv.blog-pager { background: white }\ndiv.blog-pager a.home-link { display:
353
+ none }\nspan#blog-pager-older-link { float: left; font-size: 20px; }\ndiv.feed-links
354
+ { display: none; }\n/* Neater looking headers */\nh3 { font-weight: normal;
355
+ }\n@font-face {\nfont-family: 'Cousine';\nfont-style: normal;\nfont-weight:
356
+ normal;\nsrc: local('Cousine'), url('//themes.googleusercontent.com/static/fonts/cousine/v2/S-JOYqoHekxYm2RHFzWYtgLUuEpTyoUstqEm5AMlJo4.woff')
357
+ format('woff');\n}\ncode {\nfont-family: Cousine;\nfont-size: 0.8em;\ncolor:
358
+ black;\n}\n/* Pretty printing styles. Used with prettify.js. */\n.str { color:
359
+ #080; }\n.kwd { color: #008; }\n.com { color: #800; }\n.typ { color: #606;
360
+ }\n.lit { color: #066; }\n.pun { color: #660; }\n.pln { color: #000; }\n.tag
361
+ { color: #008; }\n.atn { color: #606; }\n.atv { color: #080; }\n.dec { color:
362
+ #606; }\npre.prettyprint {\npadding: 6px;\nfont-family: Cousine;\nfont-size:
363
+ 12px;\nbackground: #eee;\n}\n@media print {\n.str { color: #060; }\n.kwd {
364
+ color: #006; font-weight: bold; }\n.com { color: #600; font-style: italic;
365
+ }\n.typ { color: #404; font-weight: bold; }\n.lit { color: #044; }\n.pun {
366
+ color: #440; }\n.pln { color: #000; }\n.tag { color: #006; font-weight: bold;
367
+ }\n.atn { color: #404; }\n.atv { color: #060; }\n}\n#navbar-iframe {\ndisplay:
368
+ none !important;\n}\n/* Links */\na:hover { text-decoration: none; border-bottom:
369
+ dotted #3d85c6 1px; }\n.post-title a:link { color: black; }\n.post-title a:active
370
+ { color: black; }\n.post-title a:visited { color: black; }\n.post-title a:hover
371
+ { color: black; }\n/* Remove line between posts */\ndiv.post-footer { border-bottom:
372
+ white; }\n/* Remove footer */\nfooter { display: none; }\n/* Clean up pager
373
+ */\ndiv.blog-pager { background: white }\ndiv.blog-pager a.home-link { display:
374
+ none }\nspan#blog-pager-older-link { float: left; font-size: 20px; }\ndiv.feed-links
375
+ { display: none; }\n/* Neater looking headers */\nh3 { font-weight: bold;
376
+ }\nh1, .title, .titlewrapper, .description, .descriptionwrapper {\ndisplay:
377
+ inline;\nmargin: 0;\npadding: 0;\n}\np.description > span {\ndisplay: block;\npadding:
378
+ 0;\nmargin-top: 0px;\nfont-size: 1rem;\n}\n--></style>\n<style id='template-skin-1'
379
+ type='text/css'><!--\nbody {\nmin-width: 940px;\n}\n.content-outer, .content-fauxcolumn-outer,
380
+ .region-inner {\nmin-width: 940px;\nmax-width: 940px;\n_width: 940px;\n}\n.main-inner
381
+ .columns {\npadding-left: 0px;\npadding-right: 200px;\n}\n.main-inner .fauxcolumn-center-outer
382
+ {\nleft: 0px;\nright: 200px;\n/* IE6 does not respect left and right together
383
+ */\n_width: expression(this.parentNode.offsetWidth -\nparseInt(\"0px\") -\nparseInt(\"200px\")
384
+ + 'px');\n}\n.main-inner .fauxcolumn-left-outer {\nwidth: 0px;\n}\n.main-inner
385
+ .fauxcolumn-right-outer {\nwidth: 200px;\n}\n.main-inner .column-left-outer
386
+ {\nwidth: 0px;\nright: 100%;\nmargin-left: -0px;\n}\n.main-inner .column-right-outer
387
+ {\nwidth: 200px;\nmargin-right: -200px;\n}\n#layout {\nmin-width: 0;\n}\n#layout
388
+ .content-outer {\nmin-width: 0;\nwidth: 800px;\n}\n#layout .region-inner {\nmin-width:
389
+ 0;\nwidth: auto;\n}\n--></style>\n<link href='https://draft.blogger.com/dyn-css/authorization.css?targetBlogID=19544619&amp;zx=e0a1d691-0381-419a-86f9-de283fff86a9'
390
+ media='none' onload='if(media!=&#39;all&#39;)media=&#39;all&#39;' rel='stylesheet'/><noscript><link
391
+ href='https://draft.blogger.com/dyn-css/authorization.css?targetBlogID=19544619&amp;zx=e0a1d691-0381-419a-86f9-de283fff86a9'
392
+ rel='stylesheet'/></noscript>\n<meta name='google-adsense-platform-account'
393
+ content='ca-host-pub-1556223355139109'/>\n<meta name='google-adsense-platform-domain'
394
+ content='blogspot.com'/>\n\n</head>\n<body class='loading'>\n<div class='navbar
395
+ no-items section' id='navbar'></div>\n<div class='body-fauxcolumns'>\n<div
396
+ class='fauxcolumn-outer body-fauxcolumn-outer'>\n<div class='cap-top'>\n<div
397
+ class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n<div class='fauxborder-left'>\n<div
398
+ class='fauxborder-right'></div>\n<div class='fauxcolumn-inner'>\n</div>\n</div>\n<div
399
+ class='cap-bottom'>\n<div class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n</div>\n</div>\n<div
400
+ class='content'>\n<div class='content-fauxcolumns'>\n<div class='fauxcolumn-outer
401
+ content-fauxcolumn-outer'>\n<div class='cap-top'>\n<div class='cap-left'></div>\n<div
402
+ class='cap-right'></div>\n</div>\n<div class='fauxborder-left'>\n<div class='fauxborder-right'></div>\n<div
403
+ class='fauxcolumn-inner'>\n</div>\n</div>\n<div class='cap-bottom'>\n<div
404
+ class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n</div>\n</div>\n<div
405
+ class='content-outer'>\n<div class='content-cap-top cap-top'>\n<div class='cap-left'></div>\n<div
406
+ class='cap-right'></div>\n</div>\n<div class='fauxborder-left content-fauxborder-left'>\n<div
407
+ class='fauxborder-right content-fauxborder-right'></div>\n<div class='content-inner'>\n<header>\n<div
408
+ class='header-outer'>\n<div class='header-cap-top cap-top'>\n<div class='cap-left'></div>\n<div
409
+ class='cap-right'></div>\n</div>\n<div class='fauxborder-left header-fauxborder-left'>\n<div
410
+ class='fauxborder-right header-fauxborder-right'></div>\n<div class='region-inner
411
+ header-inner'>\n<div class='header section' id='header'><div class='widget
412
+ Header' data-version='1' id='Header1'>\n<div id='header-inner'>\n<div class='titlewrapper'>\n<h1
413
+ class='title'>\n0xFE - 11111110b - 0376\n</h1>\n</div>\n<div class='descriptionwrapper'>\n<p
414
+ class='description'><span>\n</span></p>\n</div>\n</div>\n</div></div>\n</div>\n</div>\n<div
415
+ class='header-cap-bottom cap-bottom'>\n<div class='cap-left'></div>\n<div
416
+ class='cap-right'></div>\n</div>\n</div>\n</header>\n<div class='tabs-outer'>\n<div
417
+ class='tabs-cap-top cap-top'>\n<div class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n<div
418
+ class='fauxborder-left tabs-fauxborder-left'>\n<div class='fauxborder-right
419
+ tabs-fauxborder-right'></div>\n<div class='region-inner tabs-inner'>\n<div
420
+ class='tabs no-items section' id='crosscol'></div>\n<div class='tabs no-items
421
+ section' id='crosscol-overflow'></div>\n</div>\n</div>\n<div class='tabs-cap-bottom
422
+ cap-bottom'>\n<div class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n</div>\n<div
423
+ class='main-outer'>\n<div class='main-cap-top cap-top'>\n<div class='cap-left'></div>\n<div
424
+ class='cap-right'></div>\n</div>\n<div class='fauxborder-left main-fauxborder-left'>\n<div
425
+ class='fauxborder-right main-fauxborder-right'></div>\n<div class='region-inner
426
+ main-inner'>\n<div class='columns fauxcolumns'>\n<div class='fauxcolumn-outer
427
+ fauxcolumn-center-outer'>\n<div class='cap-top'>\n<div class='cap-left'></div>\n<div
428
+ class='cap-right'></div>\n</div>\n<div class='fauxborder-left'>\n<div class='fauxborder-right'></div>\n<div
429
+ class='fauxcolumn-inner'>\n</div>\n</div>\n<div class='cap-bottom'>\n<div
430
+ class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n</div>\n<div
431
+ class='fauxcolumn-outer fauxcolumn-left-outer'>\n<div class='cap-top'>\n<div
432
+ class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n<div class='fauxborder-left'>\n<div
433
+ class='fauxborder-right'></div>\n<div class='fauxcolumn-inner'>\n</div>\n</div>\n<div
434
+ class='cap-bottom'>\n<div class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n</div>\n<div
435
+ class='fauxcolumn-outer fauxcolumn-right-outer'>\n<div class='cap-top'>\n<div
436
+ class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n<div class='fauxborder-left'>\n<div
437
+ class='fauxborder-right'></div>\n<div class='fauxcolumn-inner'>\n</div>\n</div>\n<div
438
+ class='cap-bottom'>\n<div class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n</div>\n<!--
439
+ corrects IE6 width calculation -->\n<div class='columns-inner'>\n<div class='column-center-outer'>\n<div
440
+ class='column-center-inner'>\n<div class='main section' id='main'><div class='widget
441
+ Blog' data-version='1' id='Blog1'>\n<div class='blog-posts hfeed'>\n\n <div
442
+ class=\"date-outer\">\n \n<h2 class='date-header'><span>Friday, December
443
+ 22, 2023</span></h2>\n\n <div class=\"date-posts\">\n \n<div
444
+ class='post-outer'>\n<div class='post hentry'>\n<a name='1927090407427133388'></a>\n<h3
445
+ class='post-title entry-title'>\n<a href='https://0xfe.blogspot.com/2023/12/the-firewall-guy.html'>The
446
+ Firewall Guy</a>\n</h3>\n<div class='post-header'>\n<div class='post-header-line-1'></div>\n</div>\n<div
447
+ class='post-body entry-content' id='post-body-1927090407427133388'>\n<p>About
448
+ 20 years ago, I worked as an independent consultant at a major telecom. I
449
+ wrote a program that did some stuff, and couldn't get it to communicate on
450
+ the network.</p><p>Developers there didn't get root on a machine, and there
451
+ was limited access to tooling, so I reached out to the sysadmin. A day later
452
+ he came back to me and said, \"everything here's good, check with the firewall
453
+ guy.\"</p><p>The firewall guy. It took me a day to track down the firewall
454
+ guy.</p><p>\"Umm... Mr. Firewall guy, can you help me out with this problem?\"</p><p>Firewall
455
+ guy was kinda grumpy. He begrudgingly collected some information, begrudgingly
456
+ looked at stuff. \"Firewall's fine. Talk to the network guy.\"</p><p>Two days
457
+ later, I found the network guy, who was nicer: \"Network's good. Talk to the
458
+ firewall guy.\"</p><p>\"I did a couple of days ago. Firewall guy says everything's
459
+ fine.\"</p><p>\"Okay, then you're probably resolving to the wrong addresses.
460
+ Talk to the DNS guy.\"</p><p>It took me many days to find the DNS guy. DNS
461
+ guy left six months ago. Firewall guy was now also DNS guy.</p><p>I did not
462
+ like Firewall guy, but I didn't have a choice: \"So Mr. Firewall guy, Network
463
+ guy says it might be a DNS issue.\"</p><p>\"You again. It definitely not a
464
+ Firewall or DNS problem.\"</p><p>At this point I was kinda stuck. I was accountable
465
+ for delivering something that works, but had no agency to actually deliver
466
+ the thing.</p><p>As I was thinking about what to do next, Firewall guy called
467
+ me on my desk phone: \"try your thing again.\" I tried my thing again. \"Thanks
468
+ very much, that worked! What was wrong?\"</p><p>\"It's complicated. &lt;hangs
469
+ up&gt;\" I took a peek and saw the DNS entries didn't change. It was probably
470
+ the firewall.</p><p>It took me about two days to build the thing, and eight
471
+ days to navigate their bureaucracy. I billed them for ten days of my time,
472
+ and apologized for taking so long.</p><p>The manager replied back right away:
473
+ \"Oh that was quick! No need to apologize. Our regular consulting firm estimated
474
+ 2 people 3 months. Can you come by next week for your next project?\"</p>\n<div
475
+ style='clear: both;'></div>\n</div>\n<div class='post-footer'>\n<div class='post-footer-line
476
+ post-footer-line-1'><span class='post-author vcard'>\n</span>\n<span class='post-timestamp'>\n</span>\n<span
477
+ class='post-comment-link'>\n<a class='comment-link' href='https://0xfe.blogspot.com/2023/12/the-firewall-guy.html#comment-form'
478
+ onclick=''>0\ncomments</a>\n</span>\n<span class='post-icons'>\n</span>\n<div
479
+ class='post-share-buttons goog-inline-block'>\n</div>\n</div>\n<div class='post-footer-line
480
+ post-footer-line-2'></div>\n<div class='post-footer-line post-footer-line-3'></div>\n</div>\n</div>\n</div>\n\n
481
+ \ </div></div>\n \n\n <div class=\"date-outer\">\n
482
+ \ \n<h2 class='date-header'><span>Monday, March 02, 2020</span></h2>\n\n
483
+ \ <div class=\"date-posts\">\n \n<div class='post-outer'>\n<div
484
+ class='post hentry'>\n<a name='1309421684262886131'></a>\n<h3 class='post-title
485
+ entry-title'>\n<a href='https://0xfe.blogspot.com/2020/03/generating-spectrograms-with-neural.html'>Generating
486
+ Spectrograms with Neural Networks</a>\n</h3>\n<div class='post-header'>\n<div
487
+ class='post-header-line-1'></div>\n</div>\n<div class='post-body entry-content'
488
+ id='post-body-1309421684262886131'>\n<div dir=\"ltr\" style=\"text-align:
489
+ left;\" trbidi=\"on\">\nIn a previous experiments, I used <i><a href=\"https://en.wikipedia.org/wiki/Spectrogram\">spectrograms</a></i>&nbsp;instead
490
+ of raw audio as inputs to neural networks, while training them to recognize
491
+ pitches, intervals, and chords.<br />\n<br />\nI found that feeding the networks
492
+ raw audio data got nowhere. Training was extremely slow, and losses seemed
493
+ to be bounded at unacceptably high values. After switching to spectrograms,
494
+ the networks started learning almost immediately -- it was quite remarkable!<br
495
+ />\n<br />\nThis post is about <i><b>generating</b></i> spectrograms with
496
+ neural networks.<br />\n<br />\n<table align=\"center\" cellpadding=\"0\"
497
+ cellspacing=\"0\" class=\"tr-caption-container\" style=\"margin-left: auto;
498
+ margin-right: auto; text-align: center;\"><tbody>\n<tr><td style=\"text-align:
499
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDM2rOgj22MvXc9tLVrlD3jey8LAzjuXVUJljzhyPgaqEOluN1Fmtta9Iubv_X3rmQHWTv6y236myxVY_Hv-XBEeb3zkSPVbTYSxoW3Gk18XsOf-o9e9nEUzKDmwLaj4K6McaXEg/s1600/Screen+Shot+2020-03-02+at+11.17.26+AM.png\"
500
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
501
+ data-original-height=\"684\" data-original-width=\"1316\" height=\"332\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDM2rOgj22MvXc9tLVrlD3jey8LAzjuXVUJljzhyPgaqEOluN1Fmtta9Iubv_X3rmQHWTv6y236myxVY_Hv-XBEeb3zkSPVbTYSxoW3Gk18XsOf-o9e9nEUzKDmwLaj4K6McaXEg/s640/Screen+Shot+2020-03-02+at+11.17.26+AM.png\"
502
+ width=\"640\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
503
+ center;\">These spectrograms were generated by a Neural Network</td></tr>\n</tbody></table>\n<br
504
+ />\n<h3 style=\"text-align: left;\">\nOn Spectrograms</h3>\n<div>\n<br /></div>\nSpectrograms
505
+ are 2-dimensional visual representations of slices of audio (or really, any
506
+ signal.) On the <i>x-axis</i> of a spectrogram is time, and on the <i>y-axis</i>
507
+ is frequency.<br />\n<br />\n<table align=\"center\" cellpadding=\"0\" cellspacing=\"0\"
508
+ class=\"tr-caption-container\" style=\"margin-left: auto; margin-right: auto;
509
+ text-align: center;\"><tbody>\n<tr><td style=\"text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggIrcRsL735uyROVhGVIftc7Yg8a_RMVsl50-0JS39fjJM50Oe8bemSs_009R_DDH-MYpc_8J3-T0V51mTAS5-ae785UKd4li91y_VWNg3BzNL8sZAv_3y_fXYB4uEp6esiYdJNg/s1600/Screen+Shot+2020-02-22+at+9.15.03+PM.png\"
510
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
511
+ data-original-height=\"526\" data-original-width=\"820\" height=\"256\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggIrcRsL735uyROVhGVIftc7Yg8a_RMVsl50-0JS39fjJM50Oe8bemSs_009R_DDH-MYpc_8J3-T0V51mTAS5-ae785UKd4li91y_VWNg3BzNL8sZAv_3y_fXYB4uEp6esiYdJNg/s400/Screen+Shot+2020-02-22+at+9.15.03+PM.png\"
512
+ width=\"400\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
513
+ center;\">A Violin playing A4 (440hz)</td></tr>\n</tbody></table>\n<br />\nBecause
514
+ the data is correlated well along both dimensions, spectrograms lend themselves
515
+ well to both human analysis and convolutional neural networks.<br />\n<br
516
+ />\nSo, I wondered, why can't the networks learn the spectrograms themselves?
517
+ Under the covers, spectrograms are built with <a href=\"https://en.wikipedia.org/wiki/Short-time_Fourier_transform\">STFTs</a>,
518
+ which are entirely linear operations on data -- you slide a window over the
519
+ data at some stride length, then perform a discrete Fourier transform to get
520
+ the frequency components of the window.<br />\n<br />\nSince the transformation
521
+ is entirely linear, all you need is one network layer, no activations, no
522
+ biases. This should theoretically collapse down to a simple regression problem.
523
+ Right? Let's find out.<br />\n<br />\n<h3 style=\"text-align: left;\">\nGenerating
524
+ Training Data</h3>\n<div>\n<br /></div>\nWe start by synthesizing some training
525
+ data. To keep things simple, let's assume that we want to generate spectrograms
526
+ of 25ms of audio sampled at 8khz, which is 2000 samples. Round up (in binary)
527
+ to 2048 to make things GPU friendly.<br />\n<br />\nThe underlying <a href=\"https://en.wikipedia.org/wiki/Short-time_Fourier_transform\">STFT</a>
528
+ will use a <a href=\"https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows\">hanning
529
+ window</a> of size 256, <a href=\"https://en.wikipedia.org/wiki/Fast_Fourier_transform\">FFT</a>
530
+ size of 256, and a stride length of 250, producing <b>33x129</b> images. That's
531
+ 33 frequency-domain slices (along the time axis) capped at <a href=\"https://en.wikipedia.org/wiki/Nyquist_frequency\">128hz</a>.<br
532
+ />\n<br />\n<script src=\"https://gist.github.com/0xfe/08204e72217ce29f06ac4c1ba7dd4d39.js\"></script>\n\nNote
533
+ that the spectrograms return complex values. We want to make sure the networks
534
+ can learn to completely reconstruct both the magnitude and phase portions
535
+ of the signal. Also note that we're going to teach our network how to compute
536
+ hanning windows.<br />\n<br />\nHere's the code to generate the training data
537
+ -- we calculate <i>batch_size</i> (15,000) examples, each with 2048 samples
538
+ and assign them to <i>xs</i>. We then calculate their spectrograms and assign
539
+ them to <i>ys</i> (the targets.)<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/0da873c4ee32ff1bbb5d22b9f0b49817.js\"></script>\n\nNote
540
+ that we separate the real and imaginary components of the spectrogram and
541
+ simply stack one atop the other. We also don't scale or normalize the data
542
+ in any way. <b>Let the network figure all that out! :-)</b><br />\n<br />\n<h3
543
+ style=\"text-align: left;\">\nBuilding the Model</h3>\n<br />\nNow the fun
544
+ part. We build a single-layer network with <i>2048</i> inputs for the audio
545
+ slice, and <i><b>row * col</b></i> outputs for the image (<b><i>times two</i></b>
546
+ to hold the real and imaginary components of the outputs.) Since the outputs
547
+ are strictly a linear function of the inputs, we don't need a bias term or
548
+ activation functions.<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/bd8db7b878cbef048543e58d850bab67.js\"></script>\n\n<br
549
+ />\nAgain, this is really just linear regression. <i>With, oh, about <b>17
550
+ million variables!</b></i><br />\n<br />\n<div class=\"separator\" style=\"clear:
551
+ both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3_UJQowtAxQB2JF104bcDRY-rslxaO7XVXZWMD-vwkZ1aOrwi2NgneYOrACxPa_V8VtmtlZ-tTbsZH1amO8D_gubfAZJ-5K0KAMOuiNCv-Ze-O8WgPjOhDTCBpPbkMznMSat8zw/s1600/Screen+Shot+2020-03-01+at+8.36.20+AM.png\"
552
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
553
+ data-original-height=\"320\" data-original-width=\"1140\" height=\"176\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3_UJQowtAxQB2JF104bcDRY-rslxaO7XVXZWMD-vwkZ1aOrwi2NgneYOrACxPa_V8VtmtlZ-tTbsZH1amO8D_gubfAZJ-5K0KAMOuiNCv-Ze-O8WgPjOhDTCBpPbkMznMSat8zw/s640/Screen+Shot+2020-03-01+at+8.36.20+AM.png\"
554
+ width=\"640\" /></a></div>\n<br />\nThis model trains very fast. In 4 epochs
555
+ (about 80 seconds), the loss drops to 3.0e-08, which is sufficient for our
556
+ experiments, and in 10 epochs (about 7 minutes), we can drop it all the way
557
+ to 2.0e-15.<br />\n<br />\n<div class=\"separator\" style=\"clear: both; text-align:
558
+ center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlCy70eiXOmrryOkFXTIj7wXf3u2krxBTDuG-L_1fquE6l-J9sRGFomBL0dG7-H691XH22nPuQrwt9Fklage-dbLNfxMnllhj2S-w9b2is7ipDtNE5MNuuTLSdSSkfOzTi8mxfTQ/s1600/Screen+Shot+2020-03-02+at+11.34.06+AM.png\"
559
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
560
+ data-original-height=\"686\" data-original-width=\"1390\" height=\"313\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlCy70eiXOmrryOkFXTIj7wXf3u2krxBTDuG-L_1fquE6l-J9sRGFomBL0dG7-H691XH22nPuQrwt9Fklage-dbLNfxMnllhj2S-w9b2is7ipDtNE5MNuuTLSdSSkfOzTi8mxfTQ/s640/Screen+Shot+2020-03-02+at+11.34.06+AM.png\"
561
+ width=\"640\" /></a></div>\n<br />\n<h3 style=\"text-align: left;\">\nThe
562
+ Real Test</h3>\n<br />\nOur model is ready. Let's see how well this does on
563
+ unseen data. We generate a slice of audio playing four tones, and compare
564
+ scipy's spectrogram function with our neural network.<br />\n<br />\n<script
565
+ src=\"https://gist.github.com/0xfe/231d285b53f15b0ace08f93934100e6c.js\"></script>\n\n<br
566
+ />\n<table align=\"center\" cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\"
567
+ style=\"margin-left: auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td
568
+ style=\"text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-ASUhhLR9dlj1s5OCAKk0f341ND0VOaDihlVFDCxqF6nYBaQ0kUZA8DdplPiOCe0g7wlQU28KEk75bXY_7cbiK1zz6KHszHnhrit5AWwqaloynohIeJWKBR8Cg6RybvNIvYXF-w/s1600/Screen+Shot+2020-03-01+at+12.28.19+PM.png\"
569
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
570
+ data-original-height=\"384\" data-original-width=\"1444\" height=\"170\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-ASUhhLR9dlj1s5OCAKk0f341ND0VOaDihlVFDCxqF6nYBaQ0kUZA8DdplPiOCe0g7wlQU28KEk75bXY_7cbiK1zz6KHszHnhrit5AWwqaloynohIeJWKBR8Cg6RybvNIvYXF-w/s640/Screen+Shot+2020-03-01+at+12.28.19+PM.png\"
571
+ width=\"640\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
572
+ center;\">Left: SciPy, Right: Neural Network</td></tr>\n</tbody></table>\n<br
573
+ />\nWow, that's actually pretty good, however when we look at a log-scaled
574
+ version, you can see noise in the network-generated one.<br />\n<br />\n<script
575
+ src=\"https://gist.github.com/0xfe/77110315b05d55bad70d9a20dcc2594e.js\"></script>\n\n<br
576
+ />\n<table align=\"center\" cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\"
577
+ style=\"margin-left: auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td
578
+ style=\"text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVkxUewAMYUH4TCma7eMbiYgKrY9dVsgrRaE_4yYizNhSg8sAStsvmBkuPtpakQBwpj_YP2jf6Z7PUsULu37T4nlDA8K1B-yArvWpHbjLoJkA5VXS0uvHvU0hWoGGRjPmenOoegA/s1600/Screen+Shot+2020-03-01+at+12.28.29+PM.png\"
579
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
580
+ data-original-height=\"398\" data-original-width=\"1434\" height=\"176\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVkxUewAMYUH4TCma7eMbiYgKrY9dVsgrRaE_4yYizNhSg8sAStsvmBkuPtpakQBwpj_YP2jf6Z7PUsULu37T4nlDA8K1B-yArvWpHbjLoJkA5VXS0uvHvU0hWoGGRjPmenOoegA/s640/Screen+Shot+2020-03-01+at+12.28.29+PM.png\"
581
+ width=\"640\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
582
+ center;\"><span style=\"font-size: 12.8px;\">Log-scaled spectrogram: Left:
583
+ SciPy, Right: Neural Network</span></td></tr>\n</tbody></table>\n<br />\nMaybe
584
+ we can train it for a bit longer and try again.<br />\n<br />\n<table align=\"center\"
585
+ cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\" style=\"margin-left:
586
+ auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td style=\"text-align:
587
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSVrD6v42Jo2FkXPdZBpxpVcmyh3NdobD8pE5hJka6FtshFQBWnI0NoBYZup-be4ME93Jc6r4POBCU4mSuv-s5WHEABdK-5f3pU6SfR0Eo16GawGAG4_G__TumqlC0ZLMJURIJcA/s1600/Screen+Shot+2020-03-01+at+12.34.27+PM.png\"
588
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
589
+ data-original-height=\"392\" data-original-width=\"1434\" height=\"174\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSVrD6v42Jo2FkXPdZBpxpVcmyh3NdobD8pE5hJka6FtshFQBWnI0NoBYZup-be4ME93Jc6r4POBCU4mSuv-s5WHEABdK-5f3pU6SfR0Eo16GawGAG4_G__TumqlC0ZLMJURIJcA/s640/Screen+Shot+2020-03-01+at+12.34.27+PM.png\"
590
+ width=\"640\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
591
+ center;\">Left: SciPy, Right: Neural Network</td></tr>\n</tbody></table>\n<br
592
+ />\nOh yeah, that's way better!<br />\n<br />\n<h3 style=\"text-align: left;\">\nPeeking
593
+ into the Model</h3>\n<br />\nOkay, so we know that this works pretty well.
594
+ It's worth taking a little time to dig in and see what exactly it learned.
595
+ The best way to do this is by slicing through the layers and examining the
596
+ weight matrices.<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/af81df7faaae90a217e2a48b5251b63a.js\"></script>\n\n<br
597
+ />\nLucky for us there's just one layer with (2048, 8514) weights. The second
598
+ dimension (8154) is just the flattened spectrogram for each sample in the
599
+ first. In the code above, we reshaped and transformed the data to make it
600
+ easy to visualize.<br />\n<br />\nHere it is below -- the weight maps for
601
+ the first, 11th, 21st, and 31st slices (out of 33) of the output.<br />\n<br
602
+ />\n<div class=\"separator\" style=\"clear: both; text-align: center;\">\n<a
603
+ href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRV8G03H9-Xzc1giGrnxzWeq7O2NzAsJelVTkuFl6E0rHdYSBimnTcVrYDKZ8xKDb8nZA-eqqa6XYL7s9ImXVqElW_NNZnXP2721C9aaBDGws7FE_uv02rPmAgn9Az8FElCDtjyw/s1600/Screen+Shot+2020-03-02+at+11.43.05+AM.png\"
604
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
605
+ data-original-height=\"836\" data-original-width=\"1444\" height=\"370\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRV8G03H9-Xzc1giGrnxzWeq7O2NzAsJelVTkuFl6E0rHdYSBimnTcVrYDKZ8xKDb8nZA-eqqa6XYL7s9ImXVqElW_NNZnXP2721C9aaBDGws7FE_uv02rPmAgn9Az8FElCDtjyw/s640/Screen+Shot+2020-03-02+at+11.43.05+AM.png\"
606
+ width=\"640\" /></a></div>\n<br />\nThe vertical bands represent activated
607
+ neurons. You can see how the bands move from left to right as they work on
608
+ a 256-sample slice of the audio. But more interesting is the spiral pattern
609
+ of the windows. What's going on there? Let's slice through one of the bands
610
+ and plot just the inner dimension.<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/a119bf4bfbe05eb4dc8e383f9355b37b.js\"></script>\n\n<br
611
+ />\nThis is actually pretty cool -- each of the graphs below is a Hanning-Windowed
612
+ sine wave of an integer frequency along each of the vertical bands. These
613
+ sinusoids are correlated with the audio, one-by-one, to tease out the active
614
+ frequencies in the slice of audio.<br />\n<br />\n<table align=\"center\"
615
+ cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\" style=\"margin-left:
616
+ auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td style=\"text-align:
617
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuBIsIVk6YKFkLN2Mn_j4yLWxqYNd8UV8W1l-lABrjd_tWEynRNCzQsX5JektI4FL6N0RgTyaEUTZNaILS0FTL1j5SlrxoSR_MydSeln0smNXQdk-7G9aBf3GnQYbMT_4priqZ9g/s1600/Screen+Shot+2020-03-02+at+11.47.28+AM.png\"
618
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
619
+ data-original-height=\"999\" data-original-width=\"1600\" height=\"398\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuBIsIVk6YKFkLN2Mn_j4yLWxqYNd8UV8W1l-lABrjd_tWEynRNCzQsX5JektI4FL6N0RgTyaEUTZNaILS0FTL1j5SlrxoSR_MydSeln0smNXQdk-7G9aBf3GnQYbMT_4priqZ9g/s640/Screen+Shot+2020-03-02+at+11.47.28+AM.png\"
620
+ width=\"640\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
621
+ center;\">1, 5, and 10hz Sine Waves (Windowed)</td></tr>\n</tbody></table>\n<br
622
+ />\nTo put it simply, those pretty spirally vertical bands are... <b><i>Fourier
623
+ Transforms</i></b>!<br />\n<br />\n<h3 style=\"text-align: left;\">\nLearning
624
+ the Discrete Fourier Transform</h3>\n<br />\nExploring that network was fun,
625
+ however we must go deeper. Let's build a quick network to perform a 128-point
626
+ DFT, without any windowing, and see if there's more we can learn.<br />\n<br
627
+ />\n<script src=\"https://gist.github.com/0xfe/5a029d815b9a5f22985933cee1a71562.js\"></script>\n\nThis
628
+ is a much simpler network, with only about 65k weights. It trains very fast,
629
+ and works like a charm!<br />\n<br />\n<div class=\"separator\" style=\"clear:
630
+ both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOHIjjzVGyiyn58PQvtk4JzHPvx2opmuS_jPt118WaRlYTeIdrR6d1SnT-WiOdSg-XXlEBI4-C-iuSQmhDJd_mB6UDvnmVDQf09Hja9c51BfQ7VD-9o8aM59Rdmk2AlIREqRQmag/s1600/Screen+Shot+2020-03-02+at+1.36.11+PM.png\"
631
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
632
+ data-original-height=\"346\" data-original-width=\"1126\" height=\"196\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOHIjjzVGyiyn58PQvtk4JzHPvx2opmuS_jPt118WaRlYTeIdrR6d1SnT-WiOdSg-XXlEBI4-C-iuSQmhDJd_mB6UDvnmVDQf09Hja9c51BfQ7VD-9o8aM59Rdmk2AlIREqRQmag/s640/Screen+Shot+2020-03-02+at+1.36.11+PM.png\"
633
+ width=\"640\" /></a></div>\n<br />\nDigging into the weights, you can clearly
634
+ see the complex sinusoids used to calculate the Fourier transform.<br />\n<br
635
+ />\n<script src=\"https://gist.github.com/0xfe/89a0c10da18fef15602af5b2d59a66d6.js\"></script>\n\n<br
636
+ />\n<table align=\"center\" cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\"
637
+ style=\"margin-left: auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td
638
+ style=\"text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7-rQnX9r7yc1_mRLb0ffAINnImyrTJAbtAimvFgFb2u5GkTGK-jzfIVcq5NfQVRAlMd5UJaIq8ie42XhyphenhyphenG8dGHEzO2eo3AOnT3SiMUKChTA94ZI5LCfykpHQQbW1w7wrYlq-raA/s1600/Screen+Shot+2020-03-02+at+1.41.30+PM.png\"
639
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
640
+ data-original-height=\"1172\" data-original-width=\"1108\" height=\"640\"
641
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7-rQnX9r7yc1_mRLb0ffAINnImyrTJAbtAimvFgFb2u5GkTGK-jzfIVcq5NfQVRAlMd5UJaIq8ie42XhyphenhyphenG8dGHEzO2eo3AOnT3SiMUKChTA94ZI5LCfykpHQQbW1w7wrYlq-raA/s640/Screen+Shot+2020-03-02+at+1.41.30+PM.png\"
642
+ width=\"603\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
643
+ center;\">Real (blue) and Imaginary (green) Components</td></tr>\n</tbody></table>\n<br
644
+ />\nIf you look at the weight matrix as a whole, you see the same pattern
645
+ we saw in the vertical bands of the spectrogram NN weights.<br />\n<br />\n<div
646
+ class=\"separator\" style=\"clear: both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMZpKg4uDe5zCWWXae8oWABbYGaVCSm-PeWz6UPu65ZpZ1lYzksc_LKPisXS91Ck8GbR7gKYRwiqyVcDO7fiow1UB28BuTe1FzpVrLBZlgqYCSXECqNaEBLFNuSiI8G6cmKbppLw/s1600/Screen+Shot+2020-03-02+at+1.59.01+PM.png\"
647
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
648
+ data-original-height=\"1376\" data-original-width=\"1440\" height=\"610\"
649
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMZpKg4uDe5zCWWXae8oWABbYGaVCSm-PeWz6UPu65ZpZ1lYzksc_LKPisXS91Ck8GbR7gKYRwiqyVcDO7fiow1UB28BuTe1FzpVrLBZlgqYCSXECqNaEBLFNuSiI8G6cmKbppLw/s640/Screen+Shot+2020-03-02+at+1.59.01+PM.png\"
650
+ width=\"640\" /></a></div>\n<div class=\"separator\" style=\"clear: both;
651
+ text-align: center;\">\n<br /></div>\nThere's a lot more we can explore in
652
+ these networks, but I should probably end here... this post is getting way
653
+ too long.<br />\n<br />\n<h3 style=\"text-align: left;\">\nFinal Thoughts</h3>\n<br
654
+ />\nIt's impressive how well this works, and how quickly this works. The neural
655
+ networks we trained above are relatively crude, and there are techniques we
656
+ can explore to optimize them.<br />\n<br />\nFor example, with the spectrogram
657
+ networks -- instead of having it learn each FFT band independently for each
658
+ window, we could use a different network architecture (like recurrent networks),
659
+ or implement some kind of weight sharing strategy across multiple layers.<br
660
+ />\n<br />\nEither way, let me be clear: using Neural Networks to perform
661
+ FFTs or generate spectrograms is <b style=\"font-style: italic;\">completely
662
+ impractical, </b>and you shouldn't do it. Really, don't do it! It is, however,
663
+ a great way to explore the guts of machine learning models as they learn to
664
+ perform complicated tasks.<br />\n<br />\n<br />\n<br /></div>\n<div style='clear:
665
+ both;'></div>\n</div>\n<div class='post-footer'>\n<div class='post-footer-line
666
+ post-footer-line-1'><span class='post-author vcard'>\n</span>\n<span class='post-timestamp'>\n</span>\n<span
667
+ class='post-comment-link'>\n<a class='comment-link' href='https://0xfe.blogspot.com/2020/03/generating-spectrograms-with-neural.html#comment-form'
668
+ onclick=''>0\ncomments</a>\n</span>\n<span class='post-icons'>\n</span>\n<div
669
+ class='post-share-buttons goog-inline-block'>\n</div>\n</div>\n<div class='post-footer-line
670
+ post-footer-line-2'></div>\n<div class='post-footer-line post-footer-line-3'></div>\n</div>\n</div>\n</div>\n\n
671
+ \ </div></div>\n \n\n <div class=\"date-outer\">\n
672
+ \ \n<h2 class='date-header'><span>Friday, February 28, 2020</span></h2>\n\n
673
+ \ <div class=\"date-posts\">\n \n<div class='post-outer'>\n<div
674
+ class='post hentry'>\n<a name='6954953458036352732'></a>\n<h3 class='post-title
675
+ entry-title'>\n<a href='https://0xfe.blogspot.com/2020/02/time-frequency-duality.html'>Time
676
+ Frequency Duality</a>\n</h3>\n<div class='post-header'>\n<div class='post-header-line-1'></div>\n</div>\n<div
677
+ class='post-body entry-content' id='post-body-6954953458036352732'>\n<div
678
+ dir=\"ltr\" style=\"text-align: left;\" trbidi=\"on\">\nAn particularly interesting
679
+ characteristic of Fourier transforms is <b><i>time-frequency duality.</i></b>
680
+ This duality exposes a beautiful deep symmetry between the time and frequency
681
+ domains of a signal.<br />\n<br />\nFor example, a sinusoid in the time domain
682
+ is an impulse in the frequency domain, <i><b>and vice versa</b></i>.<br />\n<br
683
+ />\nHere's what a 1-second 20hz sine wave looks like. If you play this on
684
+ your audio device, you'll hear a 20hz tone.<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/736216255e9bbcad5929350a29d1b6b6.js\"></script>\n\n<br
685
+ />\n<table align=\"center\" cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\"
686
+ style=\"margin-left: auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td
687
+ style=\"text-align: center;\"><div class=\"separator\" style=\"clear: both;
688
+ text-align: center;\">\n<br /></div>\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfuInGS7azSrsPIEHFf8IRLzX092heOhMA-TvyOl4EWhzTGibkGhEUt317yNEu0jSSE0OlKEQFtxeWHcHPF2GeSyRlN2R41kLaE4xGnwG6qJYqFEt1CHzCBKwfWvJvhvhjIlv6Sg/s1600/Screen+Shot+2020-02-27+at+5.24.39+PM.png\"
689
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
690
+ data-original-height=\"388\" data-original-width=\"1212\" height=\"204\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfuInGS7azSrsPIEHFf8IRLzX092heOhMA-TvyOl4EWhzTGibkGhEUt317yNEu0jSSE0OlKEQFtxeWHcHPF2GeSyRlN2R41kLaE4xGnwG6qJYqFEt1CHzCBKwfWvJvhvhjIlv6Sg/s640/Screen+Shot+2020-02-27+at+5.24.39+PM.png\"
691
+ width=\"640\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
692
+ center;\">20hz Sine Wave</td></tr>\n</tbody></table>\n<br />\nWhen you take
693
+ the Fourier transform of the wave, and plot the frequency domain representation
694
+ of the signal, you get an impulse in the bin representing the 20hz. (Ignore
695
+ the&nbsp;<a href=\"https://en.wikipedia.org/wiki/Spectral_leakage\">tiny neighbours</a>&nbsp;for
696
+ now.)<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/d5b29e9f9b987eeefce1148ec3698b60.js\"></script>\n\n<br
697
+ />\n<table align=\"center\" cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\"
698
+ style=\"margin-left: auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td
699
+ style=\"text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1Bs_O3s1cSwNgTZiRB0dTlIg5GsCMzVK3kCtvwJtNWtpjrmP1FLYNF5mGTLL5EgRHg8j5qvyBSvM0Fi9XTihdyXzNtBPcAsZqkDgOqJt9JnqvKg4UqK_NpVLF32Q-lI-2wNHS8g/s1600/Screen+Shot+2020-02-27+at+5.30.35+PM.png\"
700
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
701
+ data-original-height=\"396\" data-original-width=\"1224\" height=\"206\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1Bs_O3s1cSwNgTZiRB0dTlIg5GsCMzVK3kCtvwJtNWtpjrmP1FLYNF5mGTLL5EgRHg8j5qvyBSvM0Fi9XTihdyXzNtBPcAsZqkDgOqJt9JnqvKg4UqK_NpVLF32Q-lI-2wNHS8g/s640/Screen+Shot+2020-02-27+at+5.30.35+PM.png\"
702
+ width=\"640\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
703
+ center;\">Frequency Domain of 20hz Sine Wave</td></tr>\n</tbody></table>\n<br
704
+ />\nIf you play this transformed representation out to your audio device,
705
+ you'll hear a click, generated from the single impulse pushing the speaker's
706
+ diaphragm. This is effectively an impulse signal.<br />\n<br />\nOkay, let's
707
+ create an impulse signal by hand -- a string of zeros, with a 1 somewhere
708
+ in the middle. Play this on your speaker, and, again, you'll hear a click.
709
+ This signal is no different from the the previous transformed signal, except
710
+ for maybe the position of the impulse.<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/db8bbd3c1c8635e3b6fb8c2e28c93e20.js\"></script>\n\nSo,
711
+ check this out. If you take the the FFT of the impulse and plot the frequency
712
+ domain representation, you get... <i><b>a sinusoid</b></i>!<br />\n<br />\n<script
713
+ src=\"https://gist.github.com/0xfe/154b2a1b0be3fd92cdae206b829b38ba.js\"></script>\n<a
714
+ href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFWi8YhKu_nhS72RW8x_rcI2kuNQ4DqzZxYmd39x0KAtnA9PLrNaGEKSAOcTEuTvDOGnpM28adAauZqi26cMFi8MRJfCZJhVicEagWWkIlJXqP0ssEYHgdpDut5_hTAKckcJazMQ/s1600/Screen+Shot+2020-02-28+at+6.39.56+AM.png\"
715
+ imageanchor=\"1\"><img border=\"0\" data-original-height=\"392\" data-original-width=\"1208\"
716
+ height=\"208\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFWi8YhKu_nhS72RW8x_rcI2kuNQ4DqzZxYmd39x0KAtnA9PLrNaGEKSAOcTEuTvDOGnpM28adAauZqi26cMFi8MRJfCZJhVicEagWWkIlJXqP0ssEYHgdpDut5_hTAKckcJazMQ/s640/Screen+Shot+2020-02-28+at+6.39.56+AM.png\"
717
+ width=\"640\" /></a>\n<br />\n<br />\nThis works both ways. You can the the
718
+ <b><i>inverse FFT</i></b>&nbsp;of a sine wave in the frequency domain, to
719
+ produce an impulse in the time domain.<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/fe317c6a7357dea6f23d9968e98dea2e.js\"></script>\n\n<br
720
+ />\n<table align=\"center\" cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\"
721
+ style=\"margin-left: auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td
722
+ style=\"text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgS4gwS_iQX8deUHZMtLPB4Vu4B5OzEvbDx2v8hHRzMbPEFeoEC2Qq6zDb863GrX3a09DYurSEjiAIELYexDHEjVndVw2fgQWVlp3kxft8SM1Cc8A7J0gkvL2rwfCquHr480IqMQQ/s1600/Screen+Shot+2020-02-28+at+6.42.45+AM.png\"
723
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
724
+ data-original-height=\"796\" data-original-width=\"1214\" height=\"261\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgS4gwS_iQX8deUHZMtLPB4Vu4B5OzEvbDx2v8hHRzMbPEFeoEC2Qq6zDb863GrX3a09DYurSEjiAIELYexDHEjVndVw2fgQWVlp3kxft8SM1Cc8A7J0gkvL2rwfCquHr480IqMQQ/s400/Screen+Shot+2020-02-28+at+6.42.45+AM.png\"
725
+ width=\"400\" /></a>\n</td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
726
+ center;\">Inverse Fourier Transform of a Sine Wave</td></tr>\n</tbody></table>\n<br
727
+ />\nThis is a wonderfully striking phenomenon, which I think reveals a lot
728
+ about our perception of nature.<br />\n<br />\nFor example, here's another
729
+ property of time-frequency duality --&nbsp;<a href=\"https://en.wikipedia.org/wiki/Convolution\">convolutions</a>&nbsp;in
730
+ the time domain are multiplications in the frequency domain, and vice versa<i>.
731
+ </i>Because <i>multiplications require far fewer operations than convolutions</i>,
732
+ it's much simpler to operate on frequency domain representations of signals.<br
733
+ />\n<br />\nYour inner ear consists of lots of tiny hairs that vary in thickness
734
+ and resonate at different frequencies sending frequency domain representations
735
+ of sound to your brain -- i.e.,&nbsp;<i>your ear evolved a little DSP chip
736
+ in</i> it to make it easier on your brain.</div>\n<div style='clear: both;'></div>\n</div>\n<div
737
+ class='post-footer'>\n<div class='post-footer-line post-footer-line-1'><span
738
+ class='post-author vcard'>\n</span>\n<span class='post-timestamp'>\n</span>\n<span
739
+ class='post-comment-link'>\n<a class='comment-link' href='https://0xfe.blogspot.com/2020/02/time-frequency-duality.html#comment-form'
740
+ onclick=''>0\ncomments</a>\n</span>\n<span class='post-icons'>\n</span>\n<div
741
+ class='post-share-buttons goog-inline-block'>\n</div>\n</div>\n<div class='post-footer-line
742
+ post-footer-line-2'></div>\n<div class='post-footer-line post-footer-line-3'></div>\n</div>\n</div>\n</div>\n\n
743
+ \ </div></div>\n \n\n <div class=\"date-outer\">\n
744
+ \ \n<h2 class='date-header'><span>Saturday, February 22, 2020</span></h2>\n\n
745
+ \ <div class=\"date-posts\">\n \n<div class='post-outer'>\n<div
746
+ class='post hentry'>\n<a name='1894211342385136711'></a>\n<h3 class='post-title
747
+ entry-title'>\n<a href='https://0xfe.blogspot.com/2020/02/pitch-detection-with-convolutional.html'>Pitch
748
+ Detection with Convolutional Networks</a>\n</h3>\n<div class='post-header'>\n<div
749
+ class='post-header-line-1'></div>\n</div>\n<div class='post-body entry-content'
750
+ id='post-body-1894211342385136711'>\n<div dir=\"ltr\" style=\"text-align:
751
+ left;\" trbidi=\"on\">\nWhile working on <a href=\"https://pitchy.ninja/\">Pitchy
752
+ Ninja</a>&nbsp;and <a href=\"https://vexflow.com/\">Vexflow</a>, I explored
753
+ a variety of different techniques for pitch detection that would also work
754
+ well in a browser. Although, I settled on a relatively well-known algorithm,
755
+ the exploration took me down an interesting path -- I wondered if you could
756
+ build neural networks to classify pitches, intervals, and chords in recorded
757
+ audio.<br />\n<br />\nTurns out the answer is <b><i>yes.&nbsp;</i></b>To all
758
+ of them.<br />\n<br />\nThis post details some of the techniques I used to
759
+ build a pitch-detection neural network. Although I focus on single-note pitch
760
+ estimation, these methods seem to work well for multi-note chords too.<br
761
+ />\n<br />\n<h3 style=\"text-align: left;\">\nOn Pitch Estimation</h3>\n<br
762
+ />\n<a href=\"https://en.wikipedia.org/wiki/Pitch_detection_algorithm\">Pitch
763
+ detection</a> (also called <i>fundamental frequency estimation</i>) is not
764
+ an exact science. What your brain perceives as pitch is a function of lots
765
+ of different variables, from the physical materials that generate the sounds
766
+ to your body's physiological structure.<br />\n<br />\nOne would presume that
767
+ you can simply transform a signal to its frequency domain representation,
768
+ and look at the peak frequencies. This would work for a sine wave, but as
769
+ soon as you introduce any kind of timbre (e.g., when you sing, or play a note
770
+ on a guitar), the spectrum is flooded with <a href=\"https://en.wikipedia.org/wiki/Overtone\">overtones</a>
771
+ and <a href=\"https://en.wikipedia.org/wiki/Harmonic\">harmonic partials</a>.<br
772
+ />\n<br />\nHere's a 33ms spectrogram of the note A4 (440hz) played on a piano.
773
+ You can see a peak at 440hz, and another around 1760hz.<br />\n<br />\n<div
774
+ class=\"separator\" style=\"clear: both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbd4u91YSFmMe3uUXT-tsjfSVygsHoRChlZKlFKRMkN11BNIFJsqrVhRRRxnC7hCeKEmkgiv3OYFjRo395zqpz_0zUBfl9R64ryL8Qdllg22ONbb4raIC5T1zW01BA3nOtzJG7Tw/s1600/Screen+Shot+2020-02-22+at+9.14.50+PM.png\"
775
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
776
+ data-original-height=\"534\" data-original-width=\"828\" height=\"257\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbd4u91YSFmMe3uUXT-tsjfSVygsHoRChlZKlFKRMkN11BNIFJsqrVhRRRxnC7hCeKEmkgiv3OYFjRo395zqpz_0zUBfl9R64ryL8Qdllg22ONbb4raIC5T1zW01BA3nOtzJG7Tw/s400/Screen+Shot+2020-02-22+at+9.14.50+PM.png\"
777
+ width=\"400\" /></a></div>\n<br />\n<br />\nHere's the same A4 (440hz), but
778
+ on a violin.<br />\n<br />\n<div class=\"separator\" style=\"clear: both;
779
+ text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj61E0xsI4lmYpxhsVxaUh2k7woDBN220o6d_TjgnkEfDdo-yQkpUHq5hr1TYR_GCiGvKzmh9M205x2qbmeziTOydR6pRwyHp9j4X_gv4p2nfuJLPDvuVck8DR49mII0jmEAAABdA/s1600/Screen+Shot+2020-02-22+at+9.15.03+PM.png\"
780
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
781
+ data-original-height=\"526\" data-original-width=\"820\" height=\"256\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj61E0xsI4lmYpxhsVxaUh2k7woDBN220o6d_TjgnkEfDdo-yQkpUHq5hr1TYR_GCiGvKzmh9M205x2qbmeziTOydR6pRwyHp9j4X_gv4p2nfuJLPDvuVck8DR49mII0jmEAAABdA/s400/Screen+Shot+2020-02-22+at+9.15.03+PM.png\"
782
+ width=\"400\" /></a></div>\n<br />\nAnd here's a trumpet.<br />\n<br />\n<div
783
+ class=\"separator\" style=\"clear: both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOL3_A1envcr3tirSEFs5-oOWWYFXg6bgIz5EFpI6w2cQjtP-QBr8KUx1QE9tzGNyi4LSylXsY-BZ-5Aczr1-Icd6VeNjxC_TO5fe09k0jGR8JV7LwTo8rSkLRnh1E1Jd4T4CrIg/s1600/Screen+Shot+2020-02-22+at+9.15.14+PM.png\"
784
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
785
+ data-original-height=\"528\" data-original-width=\"826\" height=\"255\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOL3_A1envcr3tirSEFs5-oOWWYFXg6bgIz5EFpI6w2cQjtP-QBr8KUx1QE9tzGNyi4LSylXsY-BZ-5Aczr1-Icd6VeNjxC_TO5fe09k0jGR8JV7LwTo8rSkLRnh1E1Jd4T4CrIg/s400/Screen+Shot+2020-02-22+at+9.15.14+PM.png\"
786
+ width=\"400\" /></a></div>\n<br />\nNotice how the thicker instruments have
787
+ rich harmonic spectrums? These harmonics are what make them beautiful, and
788
+ also what make pitch detection hard.<br />\n<br />\n<h3 style=\"text-align:
789
+ left;\">\nEstimation Techniques</h3>\n<br />\nA lot of the well understood
790
+ pitch estimation algorithms resort to transformations and heuristics which
791
+ amplify the fundamental and cancel out the overtones. Some, more advanced
792
+ techniques work on (kind of) fingerprinting timbres, and then attempting to
793
+ correlate them with a signal.<br />\n<br />\nFor single tones, these techniques
794
+ work well, but they do break down in their own unique ways. After all, they're
795
+ heuristics that try to <i>estimate human perception</i>.<br />\n<br />\n<h3
796
+ style=\"text-align: left;\">\nConvolutional Networks</h3>\n<br />\nDeep <a
797
+ href=\"https://en.wikipedia.org/wiki/Convolutional_neural_network\">convolutional
798
+ networks</a> have been winning image labeling challenges for nearly a decade,
799
+ starting with <a href=\"https://en.wikipedia.org/wiki/AlexNet\">AlexNet in
800
+ 2012</a>. The key insight in these architectures is that detecting objects
801
+ require some level of locality in pattern recognition, i.e., learned features
802
+ should be agnostic to translations, rotations, intensities, etc. Convolutional
803
+ networks learn multiple layers of filters, each capturing some perceptual
804
+ element.<br />\n<br />\n<div class=\"separator\" style=\"clear: both; text-align:
805
+ center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2LwwE33Uu5V1Kjy9nZxOQZEbh8LIe4nyUp728tznKUIym01KXtfscXhi8ASVftIu-7wJ8FKfBs4YDvDCTLSl7x2kmn7QPoubI9aBJ8zajZCg9IzGcbXvRDF8DM-pO1r_7IseNqw/s1600/Screen+Shot+2020-02-22+at+9.36.01+PM.png\"
806
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
807
+ data-original-height=\"488\" data-original-width=\"1458\" height=\"214\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2LwwE33Uu5V1Kjy9nZxOQZEbh8LIe4nyUp728tznKUIym01KXtfscXhi8ASVftIu-7wJ8FKfBs4YDvDCTLSl7x2kmn7QPoubI9aBJ8zajZCg9IzGcbXvRDF8DM-pO1r_7IseNqw/s640/Screen+Shot+2020-02-22+at+9.36.01+PM.png\"
808
+ width=\"640\" /></a></div>\n<br />\n<br />\nFor example, the bottom layer
809
+ of an image recognition network might detect edges and curves, the next might
810
+ detect simple shapes, and the next would detect objects, etc. Here's an example
811
+ of extracted features from various layers (via <a href=\"https://www.groundai.com/project/deepfeat-a-bottom-up-and-top-down-saliency-model-based-on-deep-features-of-convolutional-neural-nets/\">DeepFeat</a>.)<br
812
+ />\n<br />\n<div class=\"separator\" style=\"clear: both; text-align: center;\">\n<a
813
+ href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijkByp7Wt8FbaM2YIKun1mLOehy2NOU5Oj8xgSKXrky_FeIHYqFkuxjEbtamdj_X_sC5pnC41QRUtTpUJjRXneSK7N5y4X8PiNQAKfB1Uoblcqo94HXEavYWlYQ2zYofVZHIWrdg/s1600/Screen+Shot+2020-02-22+at+9.40.06+PM.png\"
814
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
815
+ data-original-height=\"1092\" data-original-width=\"1096\" height=\"397\"
816
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijkByp7Wt8FbaM2YIKun1mLOehy2NOU5Oj8xgSKXrky_FeIHYqFkuxjEbtamdj_X_sC5pnC41QRUtTpUJjRXneSK7N5y4X8PiNQAKfB1Uoblcqo94HXEavYWlYQ2zYofVZHIWrdg/s400/Screen+Shot+2020-02-22+at+9.40.06+PM.png\"
817
+ width=\"400\" /></a></div>\n<br />\n<h3 style=\"text-align: left;\">\nConvolutional
818
+ Networks for Audio</h3>\n<br />\nFor audio feature extraction, <a href=\"https://en.wikipedia.org/wiki/Time_domain\">time
819
+ domain</a> representations don't seem to be very useful to convnets. However,
820
+ in the <a href=\"https://en.wikipedia.org/wiki/Frequency_domain\">frequency
821
+ domain</a>, convnets learn features <i><b>extremely well</b></i>. Once networks
822
+ start looking at spectrograms, all kinds of patterns start to emerge.<br />\n<br
823
+ />\nIn the next few sections, we'll build a and train a simple convolutional
824
+ network to detect fundamental frequencies across six octaves.<br />\n<br />\n<h3
825
+ style=\"text-align: left;\">\nGetting Training Data</h3>\n<br />\nTo do this
826
+ well, we need data. Labeled. <b>Lots of it!</b> There are a few paths we can
827
+ take:<br />\n<br />\n<b>Option 1</b>: Go find a whole bunch of single-tone
828
+ music online, slice it up into little bits, transcribe and label.<br />\n<br
829
+ />\n<b>Option 2</b>: Take out my trusty guitar, record, slice, and label.
830
+ Then my keyboard, and my trumpet, and my clarinet. And maybe sing too. Ugh!<br
831
+ />\n<br />\n<b>Option 3</b>: Build synthetic samples with... code!<br />\n<br
832
+ />\nSince, you know, the ultimate programmer virtue is laziness, let's go
833
+ with Option 3.<br />\n<br />\n<h3 style=\"text-align: left;\">\nTools of the
834
+ Trade</h3>\n<br />\nThe goal is to build a model that performs well, and <i>generalizes
835
+ well</i>, so we'll need to make sure that we account for enough of the variability
836
+ in real audio as we can -- which means using a variety of instruments, velocities,
837
+ effects, envelopes, and noise profiles.<br />\n<br />\nWith a good MIDI library,
838
+ a patch bank, and some savvy, we can get this done. Here's what we need:<br
839
+ />\n<ul style=\"text-align: left;\">\n<li><a href=\"https://pypi.org/project/MIDIUtil/\">MIDIUtil</a>
840
+ - Python library to generate MIDI files.</li>\n<li><a href=\"http://www.fluidsynth.org/\">FluidSynth</a>
841
+ - Renders MIDI files to raw audio.</li>\n<li><a href=\"http://www.schristiancollins.com/generaluser.php\">GeneralUser
842
+ GS</a>&nbsp;- A bank of GM instrument patches for FluidSynth.</li>\n<li><a
843
+ href=\"http://sox.sourceforge.net/sox.html\">sox</a> - To post-process the
844
+ audio (resample, normalize, etc.)</li>\n<li><a href=\"https://www.scipy.org/\">scipy.io</a>
845
+ - For generating spectrograms</li>\n<li><a href=\"https://tensorflow.org/\">Tensorflow</a>
846
+ - For building and training the models.</li>\n</ul>\n<div>\n<br />\nAll of
847
+ these are open-source and freely available. Download and install them before
848
+ proceeding.</div>\n<h3 style=\"text-align: left;\">\n</h3>\n<h3 style=\"text-align:
849
+ left;\">\n</h3>\n<h3 style=\"text-align: left;\">\n<br /></h3>\n<h3 style=\"text-align:
850
+ left;\">\nSynthesizing the Data</h3>\n<br />\nWe start with picking a bunch
851
+ of instruments encompassing a variety of different timbres and tonalities.<br
852
+ />\n<br />\n<script src=\"https://gist.github.com/0xfe/39f374a4ec66a1dc6af2a622851b2e74.js\"></script><br
853
+ />\n<div>\n<br />\nPick the notes and octaves you want to be able to classify.
854
+ I used all 12 tones between octaves 2 and 8 (and added some random detunings.)
855
+ Here's a handy class to deal with note to MIDI value conversions.<br />\n<br
856
+ />\n<br />\n<script src=\"https://gist.github.com/0xfe/0b8b66bf4e34caedcacb5894164e7c7c.js\"></script>\n\nThe
857
+ next section is where the meat of the synthesis happens. It does the following:<br
858
+ />\n<ul>\n<li>Renders the MIDI files to raw audio (wav) using FluidSynth and
859
+ a free GM sound font.</li>\n<li>Resamples to single-channel, unsigned 16-bit,
860
+ at 44.1khz, normalized.</li>\n<li>Slices the sample up into its envelope components
861
+ (attack, sustain, decay.)</li>\n<li>Detunes some of the samples to cover more
862
+ of the harmonic surface.</li>\n</ul>\n<div>\n<script src=\"https://gist.github.com/0xfe/f01e3186b14543b42303e6af96286262.js\"></script>\n</div>\n<div>\n<br
863
+ /></div>\nFinally, we use the <b>Sample</b>&nbsp;class to generate thousands
864
+ of different 33ms long MIDI files, each playing a single note.&nbsp; The labels
865
+ are part of the filename, and include the note, octave, frequency, and envelope
866
+ component.<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/29aedb7cc2733e7bde767eebcdd1d204.js\"></script>\n\n<br
867
+ />\n<br />\n<h3 style=\"text-align: left;\">\nBuilding the Network</h3>\n<div>\n<br
868
+ /></div>\n<div>\nNow that we have the training data, let's design the network.<br
869
+ />\n<br /></div>\n<div>\nI experimented with a variety of different architectures
870
+ before I got here, starting with simple dense (non-convolution) networks with
871
+ time-domain inputs, then moving on to one-dimensional LSTMs, then two-dimensional
872
+ convolutional networks (convnets) with frequency-domain inputs.</div>\n<div>\n<br
873
+ /></div>\n<div>\nAs you can guess, the 2D networks with frequency-domain inputs
874
+ worked <b>significantly better.</b> As soon as I got decent baseline performance
875
+ with them, I focused on incrementally improving accuracy by reducing validation
876
+ loss.</div>\n<div>\n<br /></div>\n<h3 style=\"text-align: left;\">\nModel
877
+ Inputs</h3>\n<div>\n<br /></div>\n<div>\nThe inputs to the network will be
878
+ <a href=\"https://en.wikipedia.org/wiki/Spectrogram\">spectrograms</a>, which
879
+ are 2D images representing a slice of audio. The X-axis is usually time, and
880
+ the Y-axis is frequency. They're great for visualizing audio spectrums, but
881
+ also for more advanced audio analysis.<br />\n<br />\n<table align=\"center\"
882
+ cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\" style=\"margin-left:
883
+ auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td style=\"text-align:
884
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfBj4NcA-QJtuTsDM3Rv6rNxZJumr7qXjNgspGKe9lOnv9XRUjsjfqZZ3GSa50F76ayZn7BMIPcjV6MWOsihu8z4Pb0vBijwEQrV5x8VEs8zhe5llGV766O1lmXrJS0VidEAisJw/s1600/Screen+Shot+2020-02-22+at+9.17.27+PM.png\"
885
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
886
+ data-original-height=\"526\" data-original-width=\"840\" height=\"250\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfBj4NcA-QJtuTsDM3Rv6rNxZJumr7qXjNgspGKe9lOnv9XRUjsjfqZZ3GSa50F76ayZn7BMIPcjV6MWOsihu8z4Pb0vBijwEQrV5x8VEs8zhe5llGV766O1lmXrJS0VidEAisJw/s400/Screen+Shot+2020-02-22+at+9.17.27+PM.png\"
887
+ width=\"400\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
888
+ center;\">A Church Organ playing A4 (440hz)</td></tr>\n</tbody></table>\n<br
889
+ /></div>\n<div>\n<br /></div>\n<div>\nSpectrograms are typically generated
890
+ with <a href=\"https://en.wikipedia.org/wiki/Short-time_Fourier_transform\">Short
891
+ Time Fourier Transforms (STFTs)</a>. In short, the algorithm slides a window
892
+ over the audio, running <a href=\"https://en.wikipedia.org/wiki/Fast_Fourier_transform\">FFTs</a>
893
+ over the windowed data. Depending on the parameters of the STFT (and the associated
894
+ FFTs), the precision of the detected frequencies can be tweaked to match the
895
+ use case.</div>\n<br />\nFor this experiment, we're working with 44.1khz 16-bit
896
+ samples, 33ms long -- which is about 14,500 data points per sample. We first
897
+ <b>downsample</b> the audio to 16khz, yielding 5280 data points per sample.<br
898
+ />\n<br />\n<script src=\"https://gist.github.com/0xfe/86a9c94f75bd06bde76cb6a612744d49.js\"></script>\n\nThe
899
+ spectrogram will be generated via STFT, using a window size of 256, an overlap
900
+ of 200, and a 1024 point FFT zero-padded on both sides. This yields one <b>513x90</b>
901
+ pixel image per sample.<br />\n<br />\nThe 1024-point FFT also <b><i>caps
902
+ the resolution</i></b> to about 19hz, which isn't perfect, but fine for distinguishing
903
+ pitches.<br />\n<br />\n<h3 style=\"text-align: left;\">\n\nThe Network Model</h3>\n<br
904
+ />\nOur network consists of 4 convolutional layers, with 64, 128, 128, and
905
+ 256 filters respectively, which are then immediately downsampled with <a href=\"https://computersciencewiki.org/index.php/Max-pooling_/_Pooling\">max-pooling</a>
906
+ layers. The input layer reshapes the input tensors by adding a <i>channels</i>
907
+ dimension for <a href=\"https://www.tensorflow.org/api_docs/python/tf/keras/layers/Conv2D\">Conv2D</a>.
908
+ We close out the model with two densely connected layers, and a final output
909
+ node for the floating-point frequency.<br />\n<br />\nTo prevent overfitting,
910
+ we <i><a href=\"https://en.wikipedia.org/wiki/Regularization_(mathematics)\">regularize</a></i>
911
+ by aggressively adding <a href=\"https://www.tensorflow.org/api_docs/python/tf/keras/layers/Dropout\">dropout</a>
912
+ layers, including one right at the input which also doubles as an ad-hoc noise
913
+ generator.<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/56f14b33ac8e09e94d65e77f50a61280.js\"></script>\n\n<br
914
+ />\n<div>\n<br /></div>\nAlthough we use <a href=\"https://en.wikipedia.org/wiki/Mean_squared_error\">mean-squared-error</a>
915
+ as our loss function, it's the <a href=\"https://en.wikipedia.org/wiki/Mean_absolute_error\">mean-absolute-error</a>
916
+ that we need to watch, since it's easier to reason about. Let's take a look
917
+ at the model summary.<br />\n<br />\n<div class=\"separator\" style=\"clear:
918
+ both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLUdV6YW8fwFUwMsC0TGRx55VxkPmtXTC8mmDgu8iqKbGveU6Yz3pyyNTSRpX5rvgA2dtL6948KcbyiO1-h52ryC_R7KXom92l89UwACMadN_J9EmD7fB5_i72_ai6QtLRDGufTg/s1600/Screen+Shot+2020-02-22+at+5.08.33+PM.png\"
919
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
920
+ data-original-height=\"1364\" data-original-width=\"1124\" height=\"640\"
921
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLUdV6YW8fwFUwMsC0TGRx55VxkPmtXTC8mmDgu8iqKbGveU6Yz3pyyNTSRpX5rvgA2dtL6948KcbyiO1-h52ryC_R7KXom92l89UwACMadN_J9EmD7fB5_i72_ai6QtLRDGufTg/s640/Screen+Shot+2020-02-22+at+5.08.33+PM.png\"
922
+ width=\"524\" /></a></div>\n<br />\n<br />\nWow, <b>12 million</b> parameters!
923
+ Feels like a lot for an experiment, but it turns out we can build a model
924
+ in less than 10 minutes on a modern GPU. Let's start training.<br />\n<br
925
+ />\n<script src=\"https://gist.github.com/0xfe/6a7234fe6e368bbd346f5fdddd88fa12.js\"></script>\n\n<br
926
+ />\nAfter 100 epochs, we can achieve a validation MSE of 0.002, and a validation
927
+ MAE of 0.03.<br />\n<br />\n<div class=\"separator\" style=\"clear: both;
928
+ text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW4345UjhQ7l9tyvSRWymJ0Ic-usaJeM-C6VcBKpNU_Al2ur3xoDRFSjz-xtEOsfqiCUbTxn1yd91VDmWVvZAOWbS-0dGfV4gUtnH5sxDSHAdeZ_ToAK4c8L_kYPEDBZ9yS-MIYA/s1600/Screen+Shot+2020-02-22+at+11.03.15+PM.png\"
929
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
930
+ data-original-height=\"1066\" data-original-width=\"814\" height=\"400\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW4345UjhQ7l9tyvSRWymJ0Ic-usaJeM-C6VcBKpNU_Al2ur3xoDRFSjz-xtEOsfqiCUbTxn1yd91VDmWVvZAOWbS-0dGfV4gUtnH5sxDSHAdeZ_ToAK4c8L_kYPEDBZ9yS-MIYA/s400/Screen+Shot+2020-02-22+at+11.03.15+PM.png\"
931
+ width=\"305\" /></a></div>\n<br />\nYou may be wondering why the validation
932
+ MAE is so much better than the training MAE. This is because of the aggressive
933
+ dropout regularization. Dropout layers are only activated during training,
934
+ not prediction.<br />\n<br />\nThese results are quite promising for an experiment!
935
+ For classification problems, we could use <a href=\"https://en.wikipedia.org/wiki/Confusion_matrix\">confusion
936
+ matrices</a> to see where the models mispredict. For regression problems (like
937
+ this one), we can explore the losses a bit more by plotting a graph of errors
938
+ by pitch.<br />\n<br />\n<script src=\"https://gist.github.com/0xfe/3e4d90436dce46ec121b9f94948001a1.js\"></script>\n\n<br
939
+ />\n<div class=\"separator\" style=\"clear: both; text-align: center;\">\n<br
940
+ /></div>\n<div class=\"separator\" style=\"clear: both; text-align: center;\">\n<br
941
+ /></div>\n<table align=\"center\" cellpadding=\"0\" cellspacing=\"0\" class=\"tr-caption-container\"
942
+ style=\"margin-left: auto; margin-right: auto; text-align: center;\"><tbody>\n<tr><td
943
+ style=\"text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFvd_SBwOkcp64POT5Y_z7-tn1eNFhP4vMU8nPBGWtXkAv-Cn8XvQjzPc4VuFLgHNKSPSpPDJXB5MbwS0RmijcQoRiUtGZ4HZPAr_QM1mdBYaerG7V1OYCNXviTT0iJvDFF_6jGg/s1600/Screen+Shot+2020-02-22+at+10.25.46+PM.png\"
944
+ imageanchor=\"1\" style=\"margin-left: auto; margin-right: auto;\"><img border=\"0\"
945
+ data-original-height=\"512\" data-original-width=\"786\" height=\"260\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFvd_SBwOkcp64POT5Y_z7-tn1eNFhP4vMU8nPBGWtXkAv-Cn8XvQjzPc4VuFLgHNKSPSpPDJXB5MbwS0RmijcQoRiUtGZ4HZPAr_QM1mdBYaerG7V1OYCNXviTT0iJvDFF_6jGg/s400/Screen+Shot+2020-02-22+at+10.25.46+PM.png\"
946
+ width=\"400\" /></a></td></tr>\n<tr><td class=\"tr-caption\" style=\"text-align:
947
+ center;\">Prediction Errors by Pitch</td></tr>\n</tbody></table>\n<br />\nAlready,
948
+ we can see that the prediction errors are on the highest octaves. This is
949
+ very likely due to our downsampling to 16khz, causing <a href=\"https://en.wikipedia.org/wiki/Aliasing\">aliasing</a>
950
+ in the harmonics and confusing the model.<br />\n<br />\nAfter discarding
951
+ the last octave, we can take the mean of the prediction error, and what do
952
+ we see?<br />\n<div style=\"background-color: #fffffe; font-family: monospace,
953
+ Menlo, Monaco, &quot;Courier New&quot;, monospace; font-size: 14px; line-height:
954
+ 19px; white-space: pre;\">\nnp.mean(np.nan_to_num(errors_by_key[<span style=\"color:
955
+ #09885a;\">0</span>:<span style=\"color: #09885a;\">80</span>]))</div>\n<span
956
+ style=\"background-color: white; color: #212121; font-family: monospace; font-size:
957
+ 14px; white-space: pre;\">19.244542657486097</span><br />\n<br />\nPretty
958
+ much exactly the resolution of the FFT we used. It's very hard to do better
959
+ given the inputs.<br />\n<br />\n<h3 style=\"text-align: left;\">\nThe Real
960
+ Test</h3>\n<div>\n<br /></div>\nSo, how does this perform in the wild? To
961
+ answer this question, I recorded a few samples of myself playing single notes
962
+ on the guitar, and pulled some youtube videos of various instruments and sliced
963
+ them up for analysis. I also crossed my fingers and sacrificed a dozen goats.<br
964
+ />\n<br />\nAs hoped, the predictions were <b><i>right within the tolerances</i></b>
965
+ of the model. Try it yourself and let me know how it works out.<br />\n<br
966
+ />\n<h3 style=\"text-align: left;\">\nImprovements and Variations</h3>\n<br
967
+ />\nThere's a few things we can do to improve what we have -- larger FFT and
968
+ window sizes, higher sample rates, better data, etc. We can also turn this
969
+ into a classification problem by using <a href=\"https://en.wikipedia.org/wiki/Softmax_function\">softmax</a>
970
+ at the bottom layer and training directly on musical pitches instead of frequencies.<br
971
+ />\n<br />\nThis experiment was part of a whole suite of models I built for
972
+ music recognition. In a future post I'll describe a more complex set of models
973
+ I built to recognize roots, intervals, and 2-4 note chords.<br />\n<br />\nUntil
974
+ then, hope you enjoyed this post. If you did, drop me a note at <a href=\"https://twitter.com/11111110b\">@11111110b</a>.<br
975
+ />\n<br />\nAll the source code for these experiments will be available on
976
+ <a href=\"https://github.com/0xfe\">my Github page</a> as soon as it's in
977
+ slightly better shape.<br />\n<br />\n<br /></div>\n</div>\n<div style='clear:
978
+ both;'></div>\n</div>\n<div class='post-footer'>\n<div class='post-footer-line
979
+ post-footer-line-1'><span class='post-author vcard'>\n</span>\n<span class='post-timestamp'>\n</span>\n<span
980
+ class='post-comment-link'>\n<a class='comment-link' href='https://0xfe.blogspot.com/2020/02/pitch-detection-with-convolutional.html#comment-form'
981
+ onclick=''>0\ncomments</a>\n</span>\n<span class='post-icons'>\n</span>\n<div
982
+ class='post-share-buttons goog-inline-block'>\n</div>\n</div>\n<div class='post-footer-line
983
+ post-footer-line-2'></div>\n<div class='post-footer-line post-footer-line-3'></div>\n</div>\n</div>\n</div>\n\n
984
+ \ </div></div>\n \n\n <div class=\"date-outer\">\n
985
+ \ \n<h2 class='date-header'><span>Wednesday, February 19, 2020</span></h2>\n\n
986
+ \ <div class=\"date-posts\">\n \n<div class='post-outer'>\n<div
987
+ class='post hentry'>\n<a name='541596065311835171'></a>\n<h3 class='post-title
988
+ entry-title'>\n<a href='https://0xfe.blogspot.com/2020/02/no-servers-just-buckets-hosting-static.html'>No
989
+ Servers, Just Buckets: Hosting Static Websites on the Cloud</a>\n</h3>\n<div
990
+ class='post-header'>\n<div class='post-header-line-1'></div>\n</div>\n<div
991
+ class='post-body entry-content' id='post-body-541596065311835171'>\n<div dir=\"ltr\"
992
+ style=\"text-align: left;\" trbidi=\"on\">\n<br />\nFor over two decades,
993
+ I've hosted websites on managed servers. Starting with web hosting providers,
994
+ going to dedicated machines, then dedicated VMs, then cloud VMs. Maintaining
995
+ these servers tend to come at a high cognitive cost -- machine and network
996
+ setup, OS patches, web server configuration, replication and high-availability,
997
+ TLS and cert management, security... the list goes on.<br />\n<br />\nLast
998
+ year, I moved [<a href=\"https://pitchy.ninja/\">almost</a>] [<a href=\"https://muthanna.com/\">all</a>]&nbsp;[<a
999
+ href=\"https://float64.dev/\">my</a>]&nbsp;[<a href=\"https://vexflow.com/\">websites</a>]
1000
+ to cloud buckets, and it has been amazing! Life just got simpler. With just
1001
+ a few commands I got:<br />\n<br />\n<ul style=\"text-align: left;\">\n<li>A
1002
+ HTTP(s) web-server hosting my content.</li>\n<li>Managed TLS certificates.</li>\n<li>Compression,
1003
+ Caching, and Content Delivery.</li>\n<li>Replication and High availability.</li>\n<li>IPv6!</li>\n<li>Fewer
1004
+ headaches, and more spending money. :-)</li>\n</ul>\n<br />\nIf you don't
1005
+ need tight control over how your data is served, I would strongly recommend
1006
+ that you host your sites on Cloud Buckets. (Yes, of course, servers are still
1007
+ involved, you just don't need to worry about them.)<br />\n<br />\nIn this
1008
+ post, I'll show you how I got the&nbsp;<a href=\"https://float64.dev/\">float64
1009
+ website</a>&nbsp;up and serving in almost no time.<br />\n<br />\n<h3 style=\"text-align:
1010
+ left;\">\nWhat are Cloud Buckets?</h3>\n<br />\nBuckets are a storage abstraction
1011
+ for blobs of data offered by cloud providers. E.g., <a href=\"https://cloud.google.com/storage\">Google
1012
+ Cloud Storage</a> or <a href=\"https://aws.amazon.com/s3/\">Amazon S3</a>.
1013
+ Put simply, they're a place in the cloud where you can store directories of
1014
+ files (typically called objects.)<br />\n<br />\nData in buckets are managed
1015
+ by cloud providers -- they take care of all the heavy lifting around storing
1016
+ the data, replicating, backing up, and serving. You can access this data with
1017
+ command line tools, via language APIs, or from the browser. You can also manage
1018
+ permissions, ownership, replication, retention, encryption, and audit controls.<br
1019
+ />\n<br />\n<h3 style=\"text-align: left;\">\n</h3>\n<h3 style=\"text-align:
1020
+ left;\">\nHosting Websites on Cloud Buckets</h3>\n<br />\nMany cloud providers
1021
+ now allow you to serve files (sometimes called bucket objects) over the web,
1022
+ and let you distribute content over their respective CDNs. For this post,
1023
+ we'll upload a website to a <a href=\"https://cloud.google.com/storage\">Google
1024
+ Cloud Storage</a> bucket and serve it over the web.<br />\n<br />\nMake sure
1025
+ you have your <a href=\"https://cloud.google.com/\">Google Cloud</a>&nbsp;account
1026
+ setup, <a href=\"https://cloud.google.com/sdk\">command-line tools installed</a>,
1027
+ and are logged in on your terminal.<br />\n<br />\n<code>\ngcloud auth login<br
1028
+ />\ngcloud config set project &lt;your-project-id&gt;</code><br />\n<code><br
1029
+ /></code>\n\nCreate your storage bucket with <a href=\"https://cloud.google.com/storage/docs/creating-buckets\">gsutil
1030
+ mb</a>. Bucket names must be globally unique, so you'll have to pick something
1031
+ no one else has used. Here I'm using <i>float64</i> as my bucket name.<br
1032
+ />\n<br />\n<code>gsutil mb gs://float64</code>\n<br />\n<code><br /></code>\nCopy
1033
+ your website content over to the bucket. We specify '-<a href=\"https://cloud.google.com/storage/docs/gsutil/commands/cp\">a
1034
+ public-read</a>' to make the objects world-readable.<br />\n<br />\n<code>gsutil
1035
+ cp -a public-read index.html style.css index.AF4C.js gs://float64</code>\n<br
1036
+ />\n<code><br /></code>\nThat's it. Your content is now available at <i>https://storage.googleapis.com/&lt;BUCKET&gt;/index.html</i>.
1037
+ Like mine is here:&nbsp;<a href=\"https://storage.googleapis.com/float64/index.html\">https://storage.googleapis.com/float64/index.html</a>.<br
1038
+ />\n<br />\n<h3 style=\"text-align: left;\">\n</h3>\n<h3 style=\"text-align:
1039
+ left;\">\nUsing your own Domain</h3>\n<br />\nTo serve data over your own
1040
+ domain using HTTPS, you need to create a <a href=\"https://cloud.google.com/load-balancing\">Cloud
1041
+ Load Balancer</a> (or use an existing one.) Go to the <a href=\"https://console.cloud.google.com/net-services/loadbalancing\">Load
1042
+ Balancer Console</a>, click \"Create Load Balancer\", and select the HTTP/HTTPS
1043
+ option.<br />\n<br />\n<div class=\"separator\" style=\"clear: both; text-align:
1044
+ center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhipsKbza7Qsm2JCe2LJwWdOlpRexWoh8IwF0JVLK9BsUaaiV9jaUctCtiSiG6FNjoHpspbjVPED27FoXPYh8L671CzC-azbBzqp3LQ0LyJtMvQw78R3yLdjX963KIRUSd87b3Azg/s1600/Screen+Shot+2020-02-19+at+8.28.36+AM.png\"
1045
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1046
+ data-original-height=\"652\" data-original-width=\"878\" height=\"295\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhipsKbza7Qsm2JCe2LJwWdOlpRexWoh8IwF0JVLK9BsUaaiV9jaUctCtiSiG6FNjoHpspbjVPED27FoXPYh8L671CzC-azbBzqp3LQ0LyJtMvQw78R3yLdjX963KIRUSd87b3Azg/s400/Screen+Shot+2020-02-19+at+8.28.36+AM.png\"
1047
+ width=\"400\" /></a></div>\n<br />\nThe balancer configuration has three main
1048
+ parts: backend, routing rules, and frontend.<br />\n<br />\nFor the backend,
1049
+ select \"backend buckets\", and pick the bucket that you just created. Check
1050
+ the 'Enable CDN' box if you want your content cached and delivered over Google's
1051
+ worldwide Content Delivery Network.<br />\n<br />\n<div class=\"separator\"
1052
+ style=\"clear: both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9YZ38AmPpWnQ2YCL3PGI9xzJSbPD4GgyN776Ha5bZ82BubM7CjwmJ7qexivA9WywQpWBsEmKoFYRLrlvIMA3tfRkt95YDp7bZUnQbMC_8vw_OxGg5xq0J7MGNC8FyLZyT5jq3wQ/s1600/Screen+Shot+2020-02-19+at+8.31.48+AM.png\"
1053
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1054
+ data-original-height=\"100\" data-original-width=\"280\" height=\"71\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9YZ38AmPpWnQ2YCL3PGI9xzJSbPD4GgyN776Ha5bZ82BubM7CjwmJ7qexivA9WywQpWBsEmKoFYRLrlvIMA3tfRkt95YDp7bZUnQbMC_8vw_OxGg5xq0J7MGNC8FyLZyT5jq3wQ/s200/Screen+Shot+2020-02-19+at+8.31.48+AM.png\"
1055
+ width=\"200\" /></a></div>\n<br />\n<br />\nFor the routing rules, simply
1056
+ use your domain name (float64.dev) in the host field, your bucket (float64)
1057
+ in the backends field, and <code><b>/*</b></code> in Paths to say that all
1058
+ paths get routed to your bucket.<br />\n<br />\nFinally, for the frontend,
1059
+ add a new IP address, and point your domain's <i>A</i> record at it. If you're
1060
+ with the times, you can also add an IPv6 address, and point your domain's
1061
+ <i>AAAA</i> record at it.<br />\n<br />\n<div class=\"separator\" style=\"clear:
1062
+ both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZYmJHBFv8QyXiXv30K_XVUf62MvxDLSmb4GijnBsHnc2vouiVM2bAqEtM0qpjfoj7_cCcg1p17ZVXntlwmSzctH0cWHKXAFRa38IfK5yS28Am_UCy1B140zhdfmzkB6AtPM0cKA/s1600/Screen+Shot+2020-02-19+at+8.29.55+AM.png\"
1063
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1064
+ data-original-height=\"272\" data-original-width=\"962\" height=\"179\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZYmJHBFv8QyXiXv30K_XVUf62MvxDLSmb4GijnBsHnc2vouiVM2bAqEtM0qpjfoj7_cCcg1p17ZVXntlwmSzctH0cWHKXAFRa38IfK5yS28Am_UCy1B140zhdfmzkB6AtPM0cKA/s640/Screen+Shot+2020-02-19+at+8.29.55+AM.png\"
1065
+ width=\"640\" /></a></div>\n<br />\n<br />\nIf you're serving over HTTPS,
1066
+ you can create a new <a href=\"https://cloud.google.com/load-balancing/docs/ssl-certificates\">managed
1067
+ certificate</a>. These certs are issued by Let's Encrypt and managed by Google
1068
+ (i.e., Goole takes care of attaching, verifying, and renewing them.) The certificates
1069
+ take about 30 minutes to propagate.<br />\n<br />\nSave and apply your changes,
1070
+ and your custom HTTPS website is up! A few more odds and ends before we call
1071
+ it a day.<br />\n<br />\n<h3 style=\"text-align: left;\">\nSetup Index and
1072
+ Error Pages</h3>\n<div>\n<br /></div>\n<div>\nYou probably don't want your
1073
+ users typing in the name of the index HTML file (<a href=\"https://float64.dev/index.html\">https://float64.dev/index.html</a>)
1074
+ every time they visit your site. You also probably want invalid URLs showing
1075
+ a pretty error page.</div>\n<br />\nYou can use <a href=\"https://cloud.google.com/storage/docs/gsutil/commands/web\"><b>gsutil
1076
+ web</b></a>&nbsp;to configure the index and 404 pages for the bucket.<br />\n<br
1077
+ />\n<code>gsutil web set gs://my-super-bucket -m index.html -e 404.html</code><br
1078
+ />\n<br />\n<h3 style=\"text-align: left;\">\nCaching, Compression, and Content
1079
+ Delivery</h3>\n<br />\nTo take advantage of Google's CDN (or even simply to
1080
+ improve bandwidth usage and latency), you should set the <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control\">Cache-Control
1081
+ headers</a> on your files. I like to keep the expiries for the index page
1082
+ short, and everything else long (of course, also adding content hashes to
1083
+ frequently modified files.)<br />\n<br />\nWe also want to make sure that
1084
+ text files are served with <i>gzip</i> compression enabled. The <code><b>-z</b></code>
1085
+ flag compresses the file, and sets the <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding\">content-encoding</a>
1086
+ to <i>gzip</i> while serving over HTTP(s).<br />\n<br />\n<code>\ngsutil -h
1087
+ \"Cache-control:public,max-age=86400\" -m \\</code><br />\n<code>&nbsp; cp
1088
+ -a public-read -z js,map,css,svg \\<br />\n&nbsp; &nbsp; $DIST/*.js $DIST/*.map
1089
+ $DIST/*.css \\<br />\n&nbsp; &nbsp; $DIST/*.jpg $DIST/*.svg $DIST/*.png $DIST/*.ico
1090
+ \\<br />\n&nbsp; &nbsp; gs://float64</code><br />\n<code><br />\ngsutil -h
1091
+ \"Cache-control:public,max-age=300\" -m \\</code><br />\n<code>&nbsp; cp -a
1092
+ public-read -z html \\</code><br />\n<code>&nbsp; $DIST/index.html gs://float64</code><br
1093
+ />\n<code><br /></code>\nIf you've made it this far, you now have a (nearly)
1094
+ production-ready website up and running. Congratulations!<br />\n<br />\n<h3
1095
+ style=\"text-align: left;\">\nSo, how much does it cost?</h3>\n<br />\nI have
1096
+ about 8 different websites running on different domains, all using managed
1097
+ certificates and the CDN, and I pay about $20 a month.<br />\n<br />\nI use
1098
+ a single load balancer ($18/mo) and one IP address ($2/mo) for all of them.
1099
+ I get about 10 - 20k requests a day across all my sites, and bandwidth costs
1100
+ are in the pennies.<br />\n<br />\nNot cheap, but not expensive either given
1101
+ the cognitive savings. And there are cheaper options (as you'll see in the
1102
+ next section).<br />\n<br />\n<h3 style=\"text-align: left;\">\nAlternatives</h3>\n<div>\n<br
1103
+ /></div>\nThere are many ways to serve web content out of storage buckets,
1104
+ and this is just one. Depending on your traffic, the number of sites you're
1105
+ running, and what kinds of tradeoffs you're willing to make, you can optimize
1106
+ costs further.<br />\n<br />\n<a href=\"https://firebase.google.com/docs/hosting\">Firebase
1107
+ Hosting</a>&nbsp;sticks all of this into one pretty package, with a lower
1108
+ upfront cost (however, the bandwidth costs are higher as your traffic increases.)<br
1109
+ />\n<br />\n<a href=\"https://www.cloudflare.com/\">Cloudflare</a>&nbsp;has
1110
+ a&nbsp;free plan and lets you stick an SSL server and CDN in front of your
1111
+ Cloud Storage bucket. However if you want dedicated certificates, they charge
1112
+ you $5 each. Also, the minimum TTL on the free plan is 2 hours, which is not
1113
+ great if you're building static Javascript applications.<br />\n<br />\nAnd
1114
+ there's <a href=\"https://aws.amazon.com/cloudfront/\">CloudFront</a>, <a
1115
+ href=\"http://www.fastly.com/\">Fastly</a>, <a href=\"https://netlify.com/\">Netlify</a>,
1116
+ all which provide various levels of managed infrastructure, still all better
1117
+ than running your own servers.<br />\n<br />\n<h3 style=\"text-align: left;\">\nCaveats</h3>\n<div>\n<br
1118
+ /></div>\n<div>\nObviously, there's no free lunch, and good engineering requires
1119
+ making tradeoffs, and here are a few things to consider before you decide
1120
+ to migrate from servers to buckets:</div>\n<div>\n<br /></div>\n<div>\n<ul
1121
+ style=\"text-align: left;\">\n<li><b>Vendor lock-in.</b> Are you okay with
1122
+ using proprietary technologies for your stack. If not, you're better off running
1123
+ your own servers.</li>\n<li><b>Control and Flexibility.</b> Do you want advanced
1124
+ routing, URL rewriting, or other custom behavior? If so you're better off
1125
+ running your own servers.</li>\n<li><b>Cost transparency.</b> Although both
1126
+ Google and Amazon do great jobs with billing and detailed price breakdowns,
1127
+ they are super complicated and can change on a whim.</li>\n</ul>\n<div>\nFor
1128
+ a lot of what I do, these downsides are well worth it. The vendor lock-in
1129
+ troubles me the most, however it's not hard to migrate this stuff to other
1130
+ providers if I need to.</div>\n</div>\n<div>\n<br /></div>\n<div>\nIf you
1131
+ liked this, check out some of <a href=\"https://0xfe.blogspot.com/\">my other
1132
+ stuff</a> on this blog.</div>\n<br />\n<br />\n<br /></div>\n<div style='clear:
1133
+ both;'></div>\n</div>\n<div class='post-footer'>\n<div class='post-footer-line
1134
+ post-footer-line-1'><span class='post-author vcard'>\n</span>\n<span class='post-timestamp'>\n</span>\n<span
1135
+ class='post-comment-link'>\n<a class='comment-link' href='https://0xfe.blogspot.com/2020/02/no-servers-just-buckets-hosting-static.html#comment-form'
1136
+ onclick=''>1 comments</a>\n</span>\n<span class='post-icons'>\n</span>\n<div
1137
+ class='post-share-buttons goog-inline-block'>\n</div>\n</div>\n<div class='post-footer-line
1138
+ post-footer-line-2'></div>\n<div class='post-footer-line post-footer-line-3'></div>\n</div>\n</div>\n</div>\n\n
1139
+ \ </div></div>\n \n\n <div class=\"date-outer\">\n
1140
+ \ \n<h2 class='date-header'><span>Tuesday, July 12, 2016</span></h2>\n\n
1141
+ \ <div class=\"date-posts\">\n \n<div class='post-outer'>\n<div
1142
+ class='post hentry'>\n<a name='421690605020819859'></a>\n<h3 class='post-title
1143
+ entry-title'>\n<a href='https://0xfe.blogspot.com/2016/07/new-in-vexflow-es6-visual-regression.html'>New
1144
+ in VexFlow: ES6, Visual Regression Tests, and more!</a>\n</h3>\n<div class='post-header'>\n<div
1145
+ class='post-header-line-1'></div>\n</div>\n<div class='post-body entry-content'
1146
+ id='post-body-421690605020819859'>\n<div dir=\"ltr\" style=\"text-align: left;\"
1147
+ trbidi=\"on\">\nLots of developments since the last time I posted about VexFlow.<br
1148
+ />\n<h3 style=\"text-align: left;\">\n</h3>\n<div>\n<br /></div>\n<h3 style=\"text-align:
1149
+ left;\">\nVexFlow is ES6</h3>\n<div>\n<br /></div>\nThanks to the heroics
1150
+ of <a href=\"http://github.com/SilverWolf90\">SilverWolf90</a> and <a href=\"http://github.com/AaronMars\">AaronMars</a>,
1151
+ and the help from many others, VexFlow's entire <code>src/</code> tree has
1152
+ been migrated to ES6. This is a huge benefit to the project and to the health
1153
+ of the codebase. Some of the wins are:<br />\n<br />\n<ul style=\"text-align:
1154
+ left;\">\n<li>Real modules, which allows us to extract explicit dependency
1155
+ information and generate graphs like <a href=\"https://github.com/0xfe/vexflow/wiki/VexFlow-Dependency-Graph\">this</a>.</li>\n<li>Const-correctness
1156
+ and predictable variable scoping with <code>const</code> and <code>let</code>.</li>\n<li>Classes,
1157
+ lambda functions, and lots of other structural enhancements that vastly improve
1158
+ the clarity and conciseness of the codebase.</li>\n</ul>\n<div>\n<div class=\"separator\"
1159
+ style=\"clear: both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3aiqkRZhH7qzKgGfHCA1yXvmcc4CvQwetLQEHvrQej3ugBVywShEIL3tPMIFw93rJU4Qf7yyerY2Ha60AgIvadGwq98fZLMrTLbzRg0h0y9v1XG_sIyFh4lVEmi2tq-HbikKDMg/s1600/Screen+Shot+2016-07-12+at+10.06.20+AM.png\"
1160
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1161
+ height=\"390\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3aiqkRZhH7qzKgGfHCA1yXvmcc4CvQwetLQEHvrQej3ugBVywShEIL3tPMIFw93rJU4Qf7yyerY2Ha60AgIvadGwq98fZLMrTLbzRg0h0y9v1XG_sIyFh4lVEmi2tq-HbikKDMg/s640/Screen+Shot+2016-07-12+at+10.06.20+AM.png\"
1162
+ width=\"640\" /></a></div>\n<br />\n<br />\nPart of the migration effort also
1163
+ involved making everything lint-clean, improving the overall style and consistency
1164
+ of the codebase -- see <a href=\"http://github.com/SilverWolf90\">SilverWolf90</a>'s
1165
+ brief document on how <a href=\"https://github.com/0xfe/vexflow/wiki/Migrating-to-ESLint\">here</a>.</div>\n<h3
1166
+ style=\"text-align: left;\">\n</h3>\n<div>\n<br /></div>\n<h3 style=\"text-align:
1167
+ left;\">\nVisual Regression Tests</h3>\n<div>\n<br /></div>\nVexFlow now has
1168
+ a visual regression test system, and all image-generating QUnit tests are
1169
+ automatically included.<br />\n<br />\nThe goal of this system is to detect
1170
+ differences in the rendered output without having to rely on human eyeballs,
1171
+ especially given the huge number of tests that exist today. It does this by
1172
+ calculating a perceptual hash (PHASH) of each test image and comparing it
1173
+ with the hash of a good known blessed image. The larger the arithmetic distance
1174
+ between the hashes, the more different are the two images.<br />\n<br />\nThe
1175
+ system also generates a diff image, which is an overlay of the two images,
1176
+ with the differences highlighted, to ease debugging. Here's an example of
1177
+ a failing test:<br />\n<br />\n<div class=\"separator\" style=\"clear: both;
1178
+ text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxeptv0TDTSQB4_RFZOumVUFEhBNkP8P3mIiTWAJSVescGm8Fx5x0-qqZYZiPSfK8Vfrc9GiNxsjhdTzalnb243CDZ_LmqI8Y-TvxblFvAxOKT_aK_j7I4q6SZ8DcdUqIGuZlSvA/s1600/Screen+Shot+2016-07-12+at+9.46.16+AM.png\"
1179
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1180
+ height=\"640\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxeptv0TDTSQB4_RFZOumVUFEhBNkP8P3mIiTWAJSVescGm8Fx5x0-qqZYZiPSfK8Vfrc9GiNxsjhdTzalnb243CDZ_LmqI8Y-TvxblFvAxOKT_aK_j7I4q6SZ8DcdUqIGuZlSvA/s640/Screen+Shot+2016-07-12+at+9.46.16+AM.png\"
1181
+ width=\"600\" /></a></div>\n<br />\n<br />\n<div class=\"separator\" style=\"clear:
1182
+ both; text-align: center;\">\n</div>\nThese tests are run automatically for
1183
+ all PRs, commits, and releases. Props to <a href=\"http://github.com/panarch\">Taehoon
1184
+ Moon</a> for migrating the regression tests from NodeJS to SlimerJS, giving
1185
+ us headless support and Travis CI integration. To find out more, read the
1186
+ Wiki page on <a href=\"https://github.com/0xfe/vexflow/wiki/Visual-Regression-Tests\">Visual
1187
+ Regression Tests</a>.<br />\n<h3 style=\"text-align: left;\">\n</h3>\n<div>\n<br
1188
+ /></div>\n<h3 style=\"text-align: left;\">\nNative SVG</h3>\n<div>\n<br /></div>\nThanks
1189
+ to the awesome contribution of <a href=\"http://github.com/gristow\">Gregory
1190
+ Ristow</a>, VexFlow now has a native SVG rendering backend, and the <a href=\"http://raphaeljs.com/\">RaphaelJS</a>
1191
+ backend has been deprecated. This not only reduces the overall size and bloat,
1192
+ but also hugely improves rendering performance.<br />\n<br />\nThe new backend
1193
+ is called <code>Rendering.Backends.SVG</code> with the code at&nbsp;<a href=\"https://github.com/0xfe/vexflow/blob/master/src/svgcontext.js\">Vex.Flow.SVGContext</a>.
1194
+ Here is a quick example of how to use the new backend:&nbsp;<a href=\"https://jsfiddle.net/nL0cn3vL/2/\">https://jsfiddle.net/nL0cn3vL/2/</a>.<br
1195
+ />\n<br />\n<h3 style=\"text-align: left;\">\nImproved Microtonal Support</h3>\n<br
1196
+ />\nVexFlow now has better support for Arabic, Turkish, and other microtonal
1197
+ music via accidentals and key signatures. Thanks to <a href=\"http://github.com/infojunkie\">infojunkie</a>
1198
+ for a lot of the heavy lifting here, and to all the contributors in the <a
1199
+ href=\"https://github.com/0xfe/vexflow/issues/318\">GitHub issue</a>.<br />\n<br
1200
+ />\n<div class=\"separator\" style=\"clear: both; text-align: center;\">\n<a
1201
+ href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKqsOnWIHkdB5VVqFfq6gj2wgiwJXzN1Sk8XR2qJX_6EZ-2xnrzBYL5eZy23MiDbm5GCvgNA_TBzjFQ8JOCoA2JlOVMD7rw3jQxk4YphClucw6TQGtnc1-ZJgNhWYtX18BKHKMcQ/s1600/Screen+Shot+2016-07-12+at+9.50.40+AM.png\"
1202
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1203
+ height=\"212\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKqsOnWIHkdB5VVqFfq6gj2wgiwJXzN1Sk8XR2qJX_6EZ-2xnrzBYL5eZy23MiDbm5GCvgNA_TBzjFQ8JOCoA2JlOVMD7rw3jQxk4YphClucw6TQGtnc1-ZJgNhWYtX18BKHKMcQ/s640/Screen+Shot+2016-07-12+at+9.50.40+AM.png\"
1204
+ width=\"640\" /></a></div>\n<br />\n<div class=\"separator\" style=\"clear:
1205
+ both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYh59DufskkVVEVorHw4Of9VzlaEjU2i2GG8p8q3-PpWfFZrbEcWs8rvfyiEvJ1gfJwvRoEjzkW88AzO4O2LUo-Vs3PU97AmA6ZINyvz5zm4vwfU_s89qNFNrcf56tys6hDXVN6w/s1600/Screen+Shot+2016-07-12+at+9.50.52+AM.png\"
1206
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1207
+ height=\"272\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYh59DufskkVVEVorHw4Of9VzlaEjU2i2GG8p8q3-PpWfFZrbEcWs8rvfyiEvJ1gfJwvRoEjzkW88AzO4O2LUo-Vs3PU97AmA6ZINyvz5zm4vwfU_s89qNFNrcf56tys6hDXVN6w/s640/Screen+Shot+2016-07-12+at+9.50.52+AM.png\"
1208
+ width=\"640\" /></a></div>\n<br />\nMicrotonal support is by no means complete,
1209
+ but this is a noteworthy step forward in the space.<br />\n<br />\n<h3 style=\"text-align:
1210
+ left;\">\nOther Stuff</h3>\n<br />\nLots of other stuff worth mentioning:<br
1211
+ />\n<br />\n<ul style=\"text-align: left;\">\n<li>Support for user interactivity
1212
+ in SVG notation. You can attach event-handlers to elements (or groups of elements)
1213
+ and dynamically modify various properties of the score.</li>\n<li>Improved
1214
+ bounding-box support.</li>\n<li>Alignment of clef, timesignature, and other
1215
+ stave modifiers during mid-measure changes.</li>\n<li>Lots of improvements
1216
+ to the build system and Travis CI integration.</li>\n<li>Lots of bug fixes
1217
+ related to beaming, tuplets, annotations, etc.</li>\n</ul>\n<div>\n<br /></div>\n<div>\nMany
1218
+ thanks to all the contributors involved!</div>\n</div>\n<div style='clear:
1219
+ both;'></div>\n</div>\n<div class='post-footer'>\n<div class='post-footer-line
1220
+ post-footer-line-1'><span class='post-author vcard'>\n</span>\n<span class='post-timestamp'>\n</span>\n<span
1221
+ class='post-comment-link'>\n<a class='comment-link' href='https://0xfe.blogspot.com/2016/07/new-in-vexflow-es6-visual-regression.html#comment-form'
1222
+ onclick=''>2\ncomments</a>\n</span>\n<span class='post-icons'>\n</span>\n<div
1223
+ class='post-share-buttons goog-inline-block'>\n</div>\n</div>\n<div class='post-footer-line
1224
+ post-footer-line-2'></div>\n<div class='post-footer-line post-footer-line-3'></div>\n</div>\n</div>\n</div>\n\n
1225
+ \ </div></div>\n \n\n <div class=\"date-outer\">\n
1226
+ \ \n<h2 class='date-header'><span>Friday, May 02, 2014</span></h2>\n\n
1227
+ \ <div class=\"date-posts\">\n \n<div class='post-outer'>\n<div
1228
+ class='post hentry'>\n<a name='2970930225553638408'></a>\n<h3 class='post-title
1229
+ entry-title'>\n<a href='https://0xfe.blogspot.com/2014/05/new-in-vexflow.html'>New
1230
+ in VexFlow (May 2014)</a>\n</h3>\n<div class='post-header'>\n<div class='post-header-line-1'></div>\n</div>\n<div
1231
+ class='post-body entry-content' id='post-body-2970930225553638408'>\nLots
1232
+ of commits into the repository lately. Thanks to Cyril Silverman for may of
1233
+ these. Here are some of the highlights:\n\n<p/>\n\n<h3>Chord Symbols</h3>\nThis
1234
+ includes subscript/superscript support in <code>TextNote</code> and support
1235
+ for common symbols (dim, half-dim, maj7, etc.)\n<p/>\n<div class=\"separator\"
1236
+ style=\"clear: both; text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhARWln9YaHdtVL5iBAiFKZHcGI85eDMyNLhN3USobfFRbFuq6FAZPsXK746YLJHrXEcb0Hpkbza1giiZ8sCb6I-1BVwuPTCNwkY8boODT0d1x7hYEnbys1OzHbdVRmwGwEYMSaSw/s1600/Screen+Shot+2014-05-02+at+10.51.12+AM.png\"
1237
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1238
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhARWln9YaHdtVL5iBAiFKZHcGI85eDMyNLhN3USobfFRbFuq6FAZPsXK746YLJHrXEcb0Hpkbza1giiZ8sCb6I-1BVwuPTCNwkY8boODT0d1x7hYEnbys1OzHbdVRmwGwEYMSaSw/s400/Screen+Shot+2014-05-02+at+10.51.12+AM.png\"
1239
+ /></a></div>\n\n<p/>\n\n<h3>Stave Line Arrows</h3>\nThis is typically used
1240
+ in instructional material.\n<p/>\n<div class=\"separator\" style=\"clear:
1241
+ both; text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNZTalVSVd5H_a4RBTaURK2A7MhCwpKTZDwqEC6j3ZALkCB3TFv4jfVQyX6GoiALkwsTkxnRK_Ntw7HuRm3S7n6ehmw8M1xa1qqysNo7HVxG06zc1y2TNE09PNp41kaGqiHo32tg/s1600/Screen+Shot+2014-05-02+at+10.50.57+AM.png\"
1242
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1243
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNZTalVSVd5H_a4RBTaURK2A7MhCwpKTZDwqEC6j3ZALkCB3TFv4jfVQyX6GoiALkwsTkxnRK_Ntw7HuRm3S7n6ehmw8M1xa1qqysNo7HVxG06zc1y2TNE09PNp41kaGqiHo32tg/s400/Screen+Shot+2014-05-02+at+10.50.57+AM.png\"
1244
+ /></a></div>\n\n\n<div class=\"separator\" style=\"clear: both; text-align:
1245
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdoHUP3nNvIHRvV0nF_w0sv1dI7Lrir4jLiyzMNMQgMUrsgH-HkATiIUUjoYYprV9rOSCkX8-UyUCRT-331MsHJ-BTkjuBh3k-Zqq019RbKHpYP3vOjZcd3mKY7p_xiUmPkJlK5Q/s1600/Screen+Shot+2014-05-02+at+10.51.06+AM.png\"
1246
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1247
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdoHUP3nNvIHRvV0nF_w0sv1dI7Lrir4jLiyzMNMQgMUrsgH-HkATiIUUjoYYprV9rOSCkX8-UyUCRT-331MsHJ-BTkjuBh3k-Zqq019RbKHpYP3vOjZcd3mKY7p_xiUmPkJlK5Q/s400/Screen+Shot+2014-05-02+at+10.51.06+AM.png\"
1248
+ /></a></div>\n\n<p/>\n\n<h3>Slurs</h3>\nFinally, we have slurs. This uses
1249
+ a new VexFlow class called <code>Curve</code>. Slurs are highly configurable.\n<p/>\n<div
1250
+ class=\"separator\" style=\"clear: both; text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwVjQOCOtb9K1roaI9Fkpdjer-LJGK_ItJNjilN5fxykR6nkpjO_ZG_Ows3HH20ix8sIOZrzb0jm5Z-VmqP2JjwAB_KuRQgvIXF618OUC7R4T6r5WfPzFq_E4ogC_Lak_wYGIkhg/s1600/Screen+Shot+2014-05-02+at+10.51.24+AM.png\"
1251
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1252
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwVjQOCOtb9K1roaI9Fkpdjer-LJGK_ItJNjilN5fxykR6nkpjO_ZG_Ows3HH20ix8sIOZrzb0jm5Z-VmqP2JjwAB_KuRQgvIXF618OUC7R4T6r5WfPzFq_E4ogC_Lak_wYGIkhg/s400/Screen+Shot+2014-05-02+at+10.51.24+AM.png\"
1253
+ /></a></div>\n\n<div class=\"separator\" style=\"clear: both; text-align:
1254
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhatECDRLVFWQyBskqVOcVUOZg66Jcywok5t9qyAfIVcqHoUfCZa1J2Ga1Gxrp3dpiFA7Zbm6I1XGGRrWBMX6fcZ5oZDxlWZu0hR1UVipMdAgmEf-96gHbWgppln4Dp2DOsBhIAxg/s1600/Screen+Shot+2014-05-02+at+10.51.29+AM.png\"
1255
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1256
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhatECDRLVFWQyBskqVOcVUOZg66Jcywok5t9qyAfIVcqHoUfCZa1J2Ga1Gxrp3dpiFA7Zbm6I1XGGRrWBMX6fcZ5oZDxlWZu0hR1UVipMdAgmEf-96gHbWgppln4Dp2DOsBhIAxg/s400/Screen+Shot+2014-05-02+at+10.51.29+AM.png\"
1257
+ /></a></div>\n\n\n<div class=\"separator\" style=\"clear: both; text-align:
1258
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc_eVlyXPeFIevIo9au9bCa75PKtHctY2oRecgdQl15Gspn0s_i7uZK_7t6UclD-UAqCXFCJrozYETXUqg45BobYrgRGs0EZkTpCMriR21P0CdLfaH5sDfSVljgqHokOQwHM7Kbw/s1600/Screen+Shot+2014-05-02+at+10.51.36+AM.png\"
1259
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1260
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc_eVlyXPeFIevIo9au9bCa75PKtHctY2oRecgdQl15Gspn0s_i7uZK_7t6UclD-UAqCXFCJrozYETXUqg45BobYrgRGs0EZkTpCMriR21P0CdLfaH5sDfSVljgqHokOQwHM7Kbw/s400/Screen+Shot+2014-05-02+at+10.51.36+AM.png\"
1261
+ /></a></div>\n\n\n<div class=\"separator\" style=\"clear: both; text-align:
1262
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiedlG-ypZuwWHvQl6PNXpcjXV-qnrHI4QbHCX4b-xPrLfwm7i9JJjhDTORtXX2WYL-N5hzGEuGK0PFvdifZmnqU-QjRA9L1HeZ2LDC5XLe0SpiprIkafTO1_CS-66aWyGiSZpNsw/s1600/Screen+Shot+2014-05-02+at+10.51.45+AM.png\"
1263
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1264
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiedlG-ypZuwWHvQl6PNXpcjXV-qnrHI4QbHCX4b-xPrLfwm7i9JJjhDTORtXX2WYL-N5hzGEuGK0PFvdifZmnqU-QjRA9L1HeZ2LDC5XLe0SpiprIkafTO1_CS-66aWyGiSZpNsw/s400/Screen+Shot+2014-05-02+at+10.51.45+AM.png\"
1265
+ /></a></div>\n\n<p/>\n\n<h3>Improved auto-positioning of Annotations and Articulations</h3>\nAnnotations
1266
+ and Articulations now self-position based on note, stem, and beam configuration.\n<p/>\n<div
1267
+ class=\"separator\" style=\"clear: both; text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU13FCOX7XCOL9mnXofqMUGuOmGJbJvbCtUIixCdxqASZmSCLZNmPQ773x2hY5lWiPHorbJak3YtUg1BuhcV1HajHha3ryLrlId4AczhzrqALlOhdvm2wyBiz7ha8SZwRliBj11A/s1600/Screen+Shot+2014-05-02+at+10.54.03+AM.png\"
1268
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1269
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU13FCOX7XCOL9mnXofqMUGuOmGJbJvbCtUIixCdxqASZmSCLZNmPQ773x2hY5lWiPHorbJak3YtUg1BuhcV1HajHha3ryLrlId4AczhzrqALlOhdvm2wyBiz7ha8SZwRliBj11A/s400/Screen+Shot+2014-05-02+at+10.54.03+AM.png\"
1270
+ /></a></div>\n\n<p/>\n\n<h3>Grace Notes</h3>\nVexFlow now has full support
1271
+ for Grace Notes. Grace Note groups can contain complex rhythmic elements,
1272
+ and are formatted using the same code as regular notes.\n\n<p/>\n<div class=\"separator\"
1273
+ style=\"clear: both; text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSotGzQBCA_ljoJpIQM6N011kQTZ5AhkdoLgqO8ORq42h05hh82Rs_q9HBUN5lEkkba8_TbMNhx04aJ48K0GyPzsH1BHXG-1cNljOjvZgVfz7qGhcHD-N8KhS1juvDqSu7ECcd6g/s1600/Screen+Shot+2014-05-02+at+10.54.30+AM.png\"
1274
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1275
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSotGzQBCA_ljoJpIQM6N011kQTZ5AhkdoLgqO8ORq42h05hh82Rs_q9HBUN5lEkkba8_TbMNhx04aJ48K0GyPzsH1BHXG-1cNljOjvZgVfz7qGhcHD-N8KhS1juvDqSu7ECcd6g/s400/Screen+Shot+2014-05-02+at+10.54.30+AM.png\"
1276
+ /></a></div>\n\n<div class=\"separator\" style=\"clear: both; text-align:
1277
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZOxD9_LTKefUh0Vs9iqFpzlLI55B7tYSYKx79Zv43GL0BhyMqmfi63i7ulWmY1FrXmN_5HnFmORvMqCNA0yUX-jQEi6MJOv4cO3HBBvde5m1VOeRPzEXxvEO_FwR2qceVl1Zixg/s1600/Screen+Shot+2014-05-02+at+10.54.37+AM.png\"
1278
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1279
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZOxD9_LTKefUh0Vs9iqFpzlLI55B7tYSYKx79Zv43GL0BhyMqmfi63i7ulWmY1FrXmN_5HnFmORvMqCNA0yUX-jQEi6MJOv4cO3HBBvde5m1VOeRPzEXxvEO_FwR2qceVl1Zixg/s400/Screen+Shot+2014-05-02+at+10.54.37+AM.png\"
1280
+ /></a></div>\n\n<p/>\n\n<h3>Auto-Beam Imnprovements</h3>\nLots more beaming
1281
+ options, including beaming over rests, stemlet rendering, and time-signature
1282
+ aware beaming.\n\n<p/>\n<div class=\"separator\" style=\"clear: both; text-align:
1283
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjULM-Ju99byoC-ygScvma7aMv67b6ruuqb27v6rZiaQf-kvPep0n_nn8SSmm1XFFdML16g8vhXjFNaJu3gfNDCqiQ5UD69eWnvx0IFj9lfC6RRhHLcmv_v8C0MeHdwizCb01XJTg/s1600/Screen+Shot+2014-05-02+at+10.54.58+AM.png\"
1284
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1285
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjULM-Ju99byoC-ygScvma7aMv67b6ruuqb27v6rZiaQf-kvPep0n_nn8SSmm1XFFdML16g8vhXjFNaJu3gfNDCqiQ5UD69eWnvx0IFj9lfC6RRhHLcmv_v8C0MeHdwizCb01XJTg/s400/Screen+Shot+2014-05-02+at+10.54.58+AM.png\"
1286
+ /></a></div>\n\n<div class=\"separator\" style=\"clear: both; text-align:
1287
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgovZ5gaOWJyLcc9DxyQX0yI54NK-hy3hSgukICa5-gMrGTaFbVR1OXhoWjS7TkX5CDtoiBRQC4S88QBNv_IHsnRU5U8nKB0H8xHPEuU2o3goALytt_ayQgkMyCNicq7dCJ8qmgQg/s1600/Screen+Shot+2014-05-02+at+10.55.15+AM.png\"
1288
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1289
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgovZ5gaOWJyLcc9DxyQX0yI54NK-hy3hSgukICa5-gMrGTaFbVR1OXhoWjS7TkX5CDtoiBRQC4S88QBNv_IHsnRU5U8nKB0H8xHPEuU2o3goALytt_ayQgkMyCNicq7dCJ8qmgQg/s400/Screen+Shot+2014-05-02+at+10.55.15+AM.png\"
1290
+ /></a></div>\n\n<div class=\"separator\" style=\"clear: both; text-align:
1291
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihdUTDYtKZhcd-HQjXBxzNPfL0VycfORZBI7xoQWZ3Re4VpaLb9Ka59EL1WivBVfmt1cERK5RhN7zs0mbvxFwzbucMkfL50UYk7epEViA7L3UDk5ihFNJIGVdHmNTgZQ0gjPGrHg/s1600/Screen+Shot+2014-05-02+at+10.55.25+AM.png\"
1292
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1293
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihdUTDYtKZhcd-HQjXBxzNPfL0VycfORZBI7xoQWZ3Re4VpaLb9Ka59EL1WivBVfmt1cERK5RhN7zs0mbvxFwzbucMkfL50UYk7epEViA7L3UDk5ihFNJIGVdHmNTgZQ0gjPGrHg/s400/Screen+Shot+2014-05-02+at+10.55.25+AM.png\"
1294
+ /></a></div>\n\n<div class=\"separator\" style=\"clear: both; text-align:
1295
+ center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7hzcAqJ2cabV3VXA9JCKovDSwx5yKTJB8lKEOmOmHZ_VKoM9uTktsiIR6f2ulKVShwrOm7v7dUuPvNPM3E5EZ-YupxVdfz9EXHRr_FMaOlhsoP5At99tKHoVBmU9CX9-YpIMjhA/s1600/Screen+Shot+2014-05-02+at+10.55.35+AM.png\"
1296
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1297
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7hzcAqJ2cabV3VXA9JCKovDSwx5yKTJB8lKEOmOmHZ_VKoM9uTktsiIR6f2ulKVShwrOm7v7dUuPvNPM3E5EZ-YupxVdfz9EXHRr_FMaOlhsoP5At99tKHoVBmU9CX9-YpIMjhA/s400/Screen+Shot+2014-05-02+at+10.55.35+AM.png\"
1298
+ /></a></div>\n\n<p/>\n<h3>Tab-Stem Features</h3>\n\nYou can (optionally) render
1299
+ Tab Stems through stave lines.\n<p/>\n\n<div class=\"separator\" style=\"clear:
1300
+ both; text-align: center;\"><a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfsQjfpN66Lpyq_IC4dTlXCypbUy3s8wEOE1Tf-j0hRUS3JkAYk2C5topEC4P0cA2ZgQ2A7gxP1djMAp8ecedWzJgaO_lHOELzrFZq8EebqIO3Y7QZxa67UAKluCDHNhv-QgNkWQ/s1600/Screen+Shot+2014-05-02+at+10.57.42+AM.png\"
1301
+ imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"><img border=\"0\"
1302
+ src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfsQjfpN66Lpyq_IC4dTlXCypbUy3s8wEOE1Tf-j0hRUS3JkAYk2C5topEC4P0cA2ZgQ2A7gxP1djMAp8ecedWzJgaO_lHOELzrFZq8EebqIO3Y7QZxa67UAKluCDHNhv-QgNkWQ/s400/Screen+Shot+2014-05-02+at+10.57.42+AM.png\"
1303
+ /></a></div>\n\n<p/>\n\nThat's all, Folks!\n<div style='clear: both;'></div>\n</div>\n<div
1304
+ class='post-footer'>\n<div class='post-footer-line post-footer-line-1'><span
1305
+ class='post-author vcard'>\n</span>\n<span class='post-timestamp'>\n</span>\n<span
1306
+ class='post-comment-link'>\n<a class='comment-link' href='https://0xfe.blogspot.com/2014/05/new-in-vexflow.html#comment-form'
1307
+ onclick=''>19\ncomments</a>\n</span>\n<span class='post-icons'>\n</span>\n<div
1308
+ class='post-share-buttons goog-inline-block'>\n</div>\n</div>\n<div class='post-footer-line
1309
+ post-footer-line-2'></div>\n<div class='post-footer-line post-footer-line-3'></div>\n</div>\n</div>\n</div>\n\n
1310
+ \ </div></div>\n \n\n <div class=\"date-outer\">\n
1311
+ \ \n<h2 class='date-header'><span>Monday, January 02, 2012</span></h2>\n\n
1312
+ \ <div class=\"date-posts\">\n \n<div class='post-outer'>\n<div
1313
+ class='post hentry'>\n<a name='6880188793332322250'></a>\n<h3 class='post-title
1314
+ entry-title'>\n<a href='https://0xfe.blogspot.com/2012/01/more-k-means-clustering-experiments-on.html'>More
1315
+ K-Means Clustering Experiments on Images</a>\n</h3>\n<div class='post-header'>\n<div
1316
+ class='post-header-line-1'></div>\n</div>\n<div class='post-body entry-content'
1317
+ id='post-body-6880188793332322250'>\nI spent a little more time experimenting
1318
+ with <a href=\"http://0xfe.blogspot.com/2011/12/k-means-clustering-and-art.html\">k-means
1319
+ clustering</a> on images and realized that I could use these clusters to recolor
1320
+ the image in interesting ways.\n<p/>\nI wrote the function <code>save_recolor</code>
1321
+ to replace pixels from the given clusters (<code>replacements</code>) with
1322
+ new ones of equal intensity, as specified by the <code>rgb_factors</code>
1323
+ vector. For example, the following code will convert pixels of the first two
1324
+ clusters to greyscale.\n<p/>\n<pre class=\"prettyprint\">\n&gt; save_recolor(\"baby.jpeg\",
1325
+ \"baby_new.jpg\", replacements=c(1,2),\n rgb_factors=c(1/3,
1326
+ 1/3, 1/3))\n</pre>\n<p/>\nIt's greyscale because the <code>rgb_factors</code>
1327
+ distributes the pixel intensity evenly among the channels. A factor of <code>c(20/100,
1328
+ 60/100, 20/100)</code> would make pixels from the cluster 60% more green.\n<p/>\nLet's
1329
+ get to some examples. Here's an unprocessed image, alongside its color clusters.
1330
+ I picked <code>k=10</code>. You can set <code>k</code> by specifying the <code>palette_size</code>
1331
+ parameter to <code>save_recolor</code>.\n\n<div class=\"separator\" style=\"clear:
1332
+ both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnRdj14UpXHdCnqTwLonrOWUuqLfH09kzSImmQpt8gaPgq8udoZZCtbCcvejK2mvW0u9RKzC355jFpINb2sxNvMccNBy5z8j477Vx0HLeTB87Mww1nldLP6aQ7qZn5mOalIi7-Tw/s1600/Screen+shot+2012-01-01+at+12.16.45+PM.png\"
1333
+ imageanchor=\"1\" style=\"margin-left:1em; margin-right:1em\"><img border=\"0\"
1334
+ height=\"274\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnRdj14UpXHdCnqTwLonrOWUuqLfH09kzSImmQpt8gaPgq8udoZZCtbCcvejK2mvW0u9RKzC355jFpINb2sxNvMccNBy5z8j477Vx0HLeTB87Mww1nldLP6aQ7qZn5mOalIi7-Tw/s400/Screen+shot+2012-01-01+at+12.16.45+PM.png\"
1335
+ width=\"400\" /></a></div>\n\n<p/>\nHere's what happens when I remove the
1336
+ red (the first cluster).\n\n<div class=\"separator\" style=\"clear: both;
1337
+ text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj21ggbKmvK0T2hmbK5tWykQeThSTgl-n-NtJnxUF0v7xQf0DAQV6IXutPz2DuPxFVqBNim_dAvmKQ3xWcGOXrN1tc9NSb-LM2rj-1sAMVV_W6YLkX3dVST_CnlzaE6D4IzfwMUFw/s1600/arkin_recolor_nored.jpg\"
1338
+ imageanchor=\"1\" style=\"margin-left:1em; margin-right:1em\"><img border=\"0\"
1339
+ height=\"400\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj21ggbKmvK0T2hmbK5tWykQeThSTgl-n-NtJnxUF0v7xQf0DAQV6IXutPz2DuPxFVqBNim_dAvmKQ3xWcGOXrN1tc9NSb-LM2rj-1sAMVV_W6YLkX3dVST_CnlzaE6D4IzfwMUFw/s400/arkin_recolor_nored.jpg\"
1340
+ width=\"300\" /></a></div>\n\n<pre class=\"prettyprint\">\n&gt; save_recolor(\"baby.jpeg\",
1341
+ \"baby_new.jpg\", replacements=1)\n</pre>\n\n\n<p/>\nIn the next image, I
1342
+ keep the red, and remove everything else.\n\n<div class=\"separator\" style=\"clear:
1343
+ both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhA27bcS661NHy3BNusm97m9j8-jR-DYdF-rRYXgjwfZcSE08rY8qwFakOGzHk35tbuanWAJBf4QRySbcDvzE0vmDvlM0S4l1AOGHb8J6NIjCeBWAaDIZNfp8WH3puF_uqPN7K4DA/s1600/arkin_recolor_red.jpeg\"
1344
+ imageanchor=\"1\" style=\"margin-left:1em; margin-right:1em\"><img border=\"0\"
1345
+ height=\"400\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhA27bcS661NHy3BNusm97m9j8-jR-DYdF-rRYXgjwfZcSE08rY8qwFakOGzHk35tbuanWAJBf4QRySbcDvzE0vmDvlM0S4l1AOGHb8J6NIjCeBWAaDIZNfp8WH3puF_uqPN7K4DA/s400/arkin_recolor_red.jpeg\"
1346
+ width=\"300\" /></a></div>\n\n<pre class=\"prettyprint\">\n&gt; save_recolor(\"baby.jpeg\",
1347
+ \"baby_new.jpg\", replacements=2:10)\n</pre>\n\n\n<p/>\nBelow, I replace the
1348
+ red cluster pixels, with green ones of corresponding intensity.\n\n<div class=\"separator\"
1349
+ style=\"clear: both; text-align: center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwxVeNhcer-um9mLCAKIdpVqaI5GsohQzeHECRjZ7bI6gDEHzsp45SuCy7GjANE9IYAXdI0qBmf4ZEO8yWwgPWWy8MOW_O8MGnr1BDR1A3P4U90SXaYy1fmBlQU8pRiSNHXZdKug/s1600/arkin_recolor_green.jpg\"
1350
+ imageanchor=\"1\" style=\"margin-left:1em; margin-right:1em\"><img border=\"0\"
1351
+ height=\"400\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwxVeNhcer-um9mLCAKIdpVqaI5GsohQzeHECRjZ7bI6gDEHzsp45SuCy7GjANE9IYAXdI0qBmf4ZEO8yWwgPWWy8MOW_O8MGnr1BDR1A3P4U90SXaYy1fmBlQU8pRiSNHXZdKug/s400/arkin_recolor_green.jpg\"
1352
+ width=\"300\" /></a></div>\n\n<pre class=\"prettyprint\">\n&gt; save_recolor(\"baby.jpeg\",
1353
+ \"baby_new.jpg\", replacements=1,\n rgb_factors=c(10/100, 80/100,
1354
+ 10/100))\n</pre>\n\n<p/>\nAnd this is a fun one: Get rid of everything, keep
1355
+ just the grass.\n\n<div class=\"separator\" style=\"clear: both; text-align:
1356
+ center;\">\n<a href=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSuSiJ8ulDFAstlCFQkYTAJIRIhYSVl_jrUYMaC-G4D9hmpIEBMnkl3DI4B54Kvi28b5q4qJPBS8M9w0OBPqLebGd5-nNN32MUJioCg-cFx3hhoEwARmexWb9kLyd20u8NIAu_Aw/s1600/arkin_recolor_grass.jpg\"
1357
+ imageanchor=\"1\" style=\"margin-left:1em; margin-right:1em\"><img border=\"0\"
1358
+ height=\"400\" src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSuSiJ8ulDFAstlCFQkYTAJIRIhYSVl_jrUYMaC-G4D9hmpIEBMnkl3DI4B54Kvi28b5q4qJPBS8M9w0OBPqLebGd5-nNN32MUJioCg-cFx3hhoEwARmexWb9kLyd20u8NIAu_Aw/s400/arkin_recolor_grass.jpg\"
1359
+ width=\"300\" /></a></div>\n\n\n<pre class=\"prettyprint\">\n&gt; save_recolor(\"baby.jpeg\",
1360
+ \"baby_new.jpg\", replacements=c(1,3:10))\n</pre>\n\n<p/>\nI tried this on
1361
+ various images, using different cluster sizes, replacements, and RGB factors,
1362
+ with lots of interesting results. Anyhow, you should experiment with this
1363
+ yourselves and let me know what you find.\n<p/>\nI should point out that nothing
1364
+ here is novel or new -- it's all well known in image processing circles. It's
1365
+ still pretty impressive what you can do when you apply simple machine learning
1366
+ algorithms to other areas.\n\n<p>\nOkay, as in all my posts, the code is available
1367
+ in my <a href=\"http://github.com/0xfe\">GitHub repository</a>:\n<p/>\n<a
1368
+ href=\"https://github.com/0xfe/experiments/blob/master/r/recolor.rscript\">https://github.com/0xfe/experiments/blob/master/r/recolor.rscript</a>\n\n<p/>\nHappy
1369
+ new year!\n<div style='clear: both;'></div>\n</div>\n<div class='post-footer'>\n<div
1370
+ class='post-footer-line post-footer-line-1'><span class='post-author vcard'>\n</span>\n<span
1371
+ class='post-timestamp'>\n</span>\n<span class='post-comment-link'>\n<a class='comment-link'
1372
+ href='https://0xfe.blogspot.com/2012/01/more-k-means-clustering-experiments-on.html#comment-form'
1373
+ onclick=''>3\ncomments</a>\n</span>\n<span class='post-icons'>\n</span>\n<div
1374
+ class='post-share-buttons goog-inline-block'>\n</div>\n</div>\n<div class='post-footer-line
1375
+ post-footer-line-2'></div>\n<div class='post-footer-line post-footer-line-3'></div>\n</div>\n</div>\n</div>\n\n
1376
+ \ </div></div>\n \n</div>\n<div class='blog-pager' id='blog-pager'>\n<span
1377
+ id='blog-pager-older-link'>\n<a class='blog-pager-older-link' href='https://0xfe.blogspot.com/search?updated-max=2012-01-02T13:34:00-05:00&amp;max-results=8'
1378
+ id='Blog1_blog-pager-older-link' title='Older Posts'>Older Posts</a>\n</span>\n<a
1379
+ class='home-link' href='https://0xfe.blogspot.com/'>Home</a>\n</div>\n<div
1380
+ class='clear'></div>\n<div class='blog-feeds'>\n<div class='feed-links'>\nSubscribe
1381
+ to:\n<a class='feed-link' href='https://0xfe.blogspot.com/feeds/posts/default'
1382
+ target='_blank' type='application/atom+xml'>Posts (Atom)</a>\n</div>\n</div>\n</div></div>\n</div>\n</div>\n<div
1383
+ class='column-left-outer'>\n<div class='column-left-inner'>\n<aside>\n</aside>\n</div>\n</div>\n<div
1384
+ class='column-right-outer'>\n<div class='column-right-inner'>\n<aside>\n<div
1385
+ class='sidebar section' id='sidebar-right-1'><div class='widget LinkList'
1386
+ data-version='1' id='LinkList2'>\n<div class='widget-content'>\n<ul>\n<li><a
1387
+ href='http://github.com/0xfe'>github/0xfe</a></li>\n</ul>\n<div class='clear'></div>\n</div>\n</div></div>\n</aside>\n</div>\n</div>\n</div>\n<div
1388
+ style='clear: both'></div>\n<!-- columns -->\n</div>\n<!-- main -->\n</div>\n</div>\n<div
1389
+ class='main-cap-bottom cap-bottom'>\n<div class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n</div>\n<footer>\n<div
1390
+ class='footer-outer'>\n<div class='footer-cap-top cap-top'>\n<div class='cap-left'></div>\n<div
1391
+ class='cap-right'></div>\n</div>\n<div class='fauxborder-left footer-fauxborder-left'>\n<div
1392
+ class='fauxborder-right footer-fauxborder-right'></div>\n<div class='region-inner
1393
+ footer-inner'>\n<div class='foot no-items section' id='footer-1'></div>\n<!--
1394
+ outside of the include in order to lock Attribution widget -->\n<div class='foot
1395
+ section' id='footer-3'><div class='widget Attribution' data-version='1' id='Attribution1'>\n<div
1396
+ class='widget-content' style='text-align: center;'>\nCopyright 2011 Mohit
1397
+ Muthanna Cheppudira. Powered by <a href='https://draft.blogger.com' target='_blank'>Blogger</a>.\n</div>\n<div
1398
+ class='clear'></div>\n</div></div>\n</div>\n</div>\n<div class='footer-cap-bottom
1399
+ cap-bottom'>\n<div class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n</div>\n</footer>\n<!--
1400
+ content -->\n</div>\n</div>\n<div class='content-cap-bottom cap-bottom'>\n<div
1401
+ class='cap-left'></div>\n<div class='cap-right'></div>\n</div>\n</div>\n</div>\n<script
1402
+ type='text/javascript'>\n window.setTimeout(function() {\n document.body.className
1403
+ = document.body.className.replace('loading', '');\n }, 10);\n </script>\n\n<script
1404
+ type=\"text/javascript\" src=\"https://www.blogger.com/static/v1/widgets/3000588928-widgets.js\"></script>\n<script
1405
+ type='text/javascript'>\nwindow['__wavt'] = 'AOuZoY7MPoJeyKYqO6N9mdnvbWds903W9g:1753780911572';_WidgetManager._Init('//draft.blogger.com/rearrange?blogID\\x3d19544619','//0xfe.blogspot.com/','19544619');\n_WidgetManager._SetDataContext([{'name':
1406
+ 'blog', 'data': {'blogId': '19544619', 'title': '0xFE - 11111110b - 0376',
1407
+ 'url': 'https://0xfe.blogspot.com/', 'canonicalUrl': 'http://0xfe.blogspot.com/',
1408
+ 'homepageUrl': 'https://0xfe.blogspot.com/', 'searchUrl': 'https://0xfe.blogspot.com/search',
1409
+ 'canonicalHomepageUrl': 'http://0xfe.blogspot.com/', 'blogspotFaviconUrl':
1410
+ 'https://0xfe.blogspot.com/favicon.ico', 'bloggerUrl': 'https://draft.blogger.com',
1411
+ 'hasCustomDomain': false, 'httpsEnabled': true, 'enabledCommentProfileImages':
1412
+ true, 'gPlusViewType': 'FILTERED_POSTMOD', 'adultContent': false, 'analyticsAccountNumber':
1413
+ '', 'encoding': 'UTF-8', 'locale': 'en', 'localeUnderscoreDelimited': 'en',
1414
+ 'languageDirection': 'ltr', 'isPrivate': false, 'isMobile': false, 'isMobileRequest':
1415
+ false, 'mobileClass': '', 'isPrivateBlog': false, 'isDynamicViewsAvailable':
1416
+ true, 'feedLinks': '\\x3clink rel\\x3d\\x22alternate\\x22 type\\x3d\\x22application/atom+xml\\x22
1417
+ title\\x3d\\x220xFE - 11111110b - 0376 - Atom\\x22 href\\x3d\\x22https://0xfe.blogspot.com/feeds/posts/default\\x22
1418
+ /\\x3e\\n\\x3clink rel\\x3d\\x22alternate\\x22 type\\x3d\\x22application/rss+xml\\x22
1419
+ title\\x3d\\x220xFE - 11111110b - 0376 - RSS\\x22 href\\x3d\\x22https://0xfe.blogspot.com/feeds/posts/default?alt\\x3drss\\x22
1420
+ /\\x3e\\n\\x3clink rel\\x3d\\x22service.post\\x22 type\\x3d\\x22application/atom+xml\\x22
1421
+ title\\x3d\\x220xFE - 11111110b - 0376 - Atom\\x22 href\\x3d\\x22https://draft.blogger.com/feeds/19544619/posts/default\\x22
1422
+ /\\x3e\\n', 'meTag': '\\x3clink rel\\x3d\\x22me\\x22 href\\x3d\\x22https://draft.blogger.com/profile/11179501091623983192\\x22
1423
+ /\\x3e\\n', 'adsenseHostId': 'ca-host-pub-1556223355139109', 'adsenseHasAds':
1424
+ false, 'adsenseAutoAds': false, 'boqCommentIframeForm': true, 'loginRedirectParam':
1425
+ '', 'view': '', 'dynamicViewsCommentsSrc': '//www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js',
1426
+ 'dynamicViewsScriptSrc': '//www.blogblog.com/dynamicviews/aec9a2fc4235c5ac',
1427
+ 'plusOneApiSrc': 'https://apis.google.com/js/platform.js', 'disableGComments':
1428
+ true, 'interstitialAccepted': false, 'sharing': {'platforms': [{'name': 'Get
1429
+ link', 'key': 'link', 'shareMessage': 'Get link', 'target': ''}, {'name':
1430
+ 'Facebook', 'key': 'facebook', 'shareMessage': 'Share to Facebook', 'target':
1431
+ 'facebook'}, {'name': 'BlogThis!', 'key': 'blogThis', 'shareMessage': 'BlogThis!',
1432
+ 'target': 'blog'}, {'name': 'X', 'key': 'twitter', 'shareMessage': 'Share
1433
+ to X', 'target': 'twitter'}, {'name': 'Pinterest', 'key': 'pinterest', 'shareMessage':
1434
+ 'Share to Pinterest', 'target': 'pinterest'}, {'name': 'Email', 'key': 'email',
1435
+ 'shareMessage': 'Email', 'target': 'email'}], 'disableGooglePlus': true, 'googlePlusShareButtonWidth':
1436
+ 0, 'googlePlusBootstrap': '\\x3cscript type\\x3d\\x22text/javascript\\x22\\x3ewindow.___gcfg
1437
+ \\x3d {\\x27lang\\x27: \\x27en\\x27};\\x3c/script\\x3e'}, 'hasCustomJumpLinkMessage':
1438
+ false, 'jumpLinkMessage': 'Read more', 'pageType': 'index', 'pageName': '',
1439
+ 'pageTitle': '0xFE - 11111110b - 0376'}}, {'name': 'features', 'data': {}},
1440
+ {'name': 'messages', 'data': {'edit': 'Edit', 'linkCopiedToClipboard': 'Link
1441
+ copied to clipboard!', 'ok': 'Ok', 'postLink': 'Post Link'}}, {'name': 'template',
1442
+ 'data': {'name': 'custom', 'localizedName': 'Custom', 'isResponsive': false,
1443
+ 'isAlternateRendering': false, 'isCustom': true}}, {'name': 'view', 'data':
1444
+ {'classic': {'name': 'classic', 'url': '?view\\x3dclassic'}, 'flipcard': {'name':
1445
+ 'flipcard', 'url': '?view\\x3dflipcard'}, 'magazine': {'name': 'magazine',
1446
+ 'url': '?view\\x3dmagazine'}, 'mosaic': {'name': 'mosaic', 'url': '?view\\x3dmosaic'},
1447
+ 'sidebar': {'name': 'sidebar', 'url': '?view\\x3dsidebar'}, 'snapshot': {'name':
1448
+ 'snapshot', 'url': '?view\\x3dsnapshot'}, 'timeslide': {'name': 'timeslide',
1449
+ 'url': '?view\\x3dtimeslide'}, 'isMobile': false, 'title': '0xFE - 11111110b
1450
+ - 0376', 'description': '', 'url': 'https://0xfe.blogspot.com/', 'type': 'feed',
1451
+ 'isSingleItem': false, 'isMultipleItems': true, 'isError': false, 'isPage':
1452
+ false, 'isPost': false, 'isHomepage': true, 'isArchive': false, 'isLabelSearch':
1453
+ false}}]);\n_WidgetManager._RegisterWidget('_HeaderView', new _WidgetInfo('Header1',
1454
+ 'header', document.getElementById('Header1'), {}, 'displayModeFull'));\n_WidgetManager._RegisterWidget('_BlogView',
1455
+ new _WidgetInfo('Blog1', 'main', document.getElementById('Blog1'), {'cmtInteractionsEnabled':
1456
+ false, 'lightboxEnabled': true, 'lightboxModuleUrl': 'https://www.blogger.com/static/v1/jsbin/249874-lbx.js',
1457
+ 'lightboxCssUrl': 'https://www.blogger.com/static/v1/v-css/123180807-lightbox_bundle.css'},
1458
+ 'displayModeFull'));\n_WidgetManager._RegisterWidget('_LinkListView', new
1459
+ _WidgetInfo('LinkList2', 'sidebar-right-1', document.getElementById('LinkList2'),
1460
+ {}, 'displayModeFull'));\n_WidgetManager._RegisterWidget('_AttributionView',
1461
+ new _WidgetInfo('Attribution1', 'footer-3', document.getElementById('Attribution1'),
1462
+ {}, 'displayModeFull'));\n</script>\n</body>\n<script language=\"javascript\"
1463
+ src=\"//google-code-prettify.googlecode.com/svn/trunk/src/prettify.js\" type=\"text/javascript\"></script>\n<script
1464
+ language=\"javascript\" src=\"//google-code-prettify.googlecode.com/svn/trunk/src/lang-css.js\"
1465
+ type=\"text/javascript\"></script>\n<script type='text/javascript'>\nfunction
1466
+ addLoadEvent(func) {\n var oldonload = window.onload;\n if (typeof window.onload
1467
+ != \"function\") {\n window.onload = func;\n } else {\n window.onload
1468
+ = function() {\n if (oldonload) {\n oldonload();\n }\n func();\n
1469
+ \ }\n }\n}\naddLoadEvent(prettyPrint);\n</script>\n</html>"
1470
+ recorded_at: Tue, 29 Jul 2025 09:54:37 GMT
1471
+ recorded_with: VCR 6.3.1