├── Fatbros.ogg
├── tests
├── spacebg.jpg
├── test11_embedded_audio.html
├── test04_audio_analyzer.html
├── test05a_colour_scale.html
├── test01_tiles.html
├── test02_hexagons.html
├── test03_cubes.html
├── test03_cubes_optimized.html
├── test05_audio_hexagons.html
├── test07_audio_hexagons_plus.html
├── test10_audio_hexagons_starfield_2.html
├── test08_audio_hexagons_centered.html
├── test12_soundcloud_visualizer.html
└── test09_audio_hexagons_starfield.html
├── assets
└── powered_by_white.png
├── index.html
├── .gitattributes
├── LICENSE.txt
├── README.md
├── .gitignore
├── webapi.html
├── css
├── normalize.css
└── style.css
└── js
└── app2.js
/Fatbros.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GabrielchenCN/yunba-wifi-bulb-example/HEAD/Fatbros.ogg
--------------------------------------------------------------------------------
/tests/spacebg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GabrielchenCN/yunba-wifi-bulb-example/HEAD/tests/spacebg.jpg
--------------------------------------------------------------------------------
/assets/powered_by_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GabrielchenCN/yunba-wifi-bulb-example/HEAD/assets/powered_by_white.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Yunba
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Michael Bromley
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## YunBa - Wi-Fi灯泡web端声控说明
2 |
3 | 使用 Web Audio API 和 [云巴智能灯泡](https://github.com/yunbaidea/yunbabulb)的Restful API,在web端通过声音控制云巴智能小灯泡的亮度和颜色。
4 |
5 | ## 使用方法
6 |
7 | ###安装
8 |
9 | 1. `git clone git@github.com:GabrielchenCN/yunba-wifi-bulb-example.git`
10 | 2. [使用nginx部署应用和跨域问题处理](http://www.cnblogs.com/gabrielchen/p/5066120.html)
11 |
12 |
13 | ###配置
14 |
15 | 1. 首先确保云巴灯泡有相应别名和正确连接上服务器,详见:[云巴智能灯泡](https://github.com/yunbaidea/yunbabulb)。
16 |
17 | 2. 在app2.js中配置frevolObj对象的正确别名。
18 |
19 | `var frevolObj={"Sarah":{"r":105,"g":"","b":255,"volume":0}}; `
20 |
21 | 3. 因为是前端AJAX请求,所以直接请求url可能遇到跨域问题。设置`urlproxy`参数,可能遇到的[跨域访问问题解决方案](http://www.cnblogs.com/gabrielchen/p/5066120.html)。
22 | - 直接请求url以及参数设置如下:
23 | - alias: 别名
24 | - p : 默认1000
25 | - rgb : 分别为对应rgb值,范围0~22222,值越大亮度越大
26 | - http method: GET
27 | - ```var url = 'http://rest.yunba.io:8080?method=publish_to_alias&appkey=56556dd4f085fc471efe0688&seckey=sec-uTYMY37JTlH5hEmsvmgO8FkZYwkkPA45VAuDifUeQIsh4enS&alias='+config.alias+'&msg={"p":'+config.p+',"r":'+config.red+',"g":'+config.green+',"b":'+config.blue+'}';```
28 |
29 | ###运行
30 |
31 | - 运行index.html
32 | - 建议使用firefox运行或47版本以下chrome,并允许浏览器共享麦克风。
33 |
34 | ###浏览器支持
35 |
36 | - chrome 47 以下(出于安全原因,47以上需要https协议访问[详见](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
37 | - firefox
38 |
39 |
40 | ###相关引用
41 |
42 | - [soundcloud-visualizer](https://github.com/michaelbromley/soundcloud-visualizer)
43 | - [web audio api 简单开始](http://www.cnblogs.com/gabrielchen/p/5078760.html)
44 |
--------------------------------------------------------------------------------
/tests/test11_embedded_audio.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/tests/test04_audio_analyzer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
45 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/tests/test05a_colour_scale.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/tests/test01_tiles.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
59 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
3 | #################
4 | ## Eclipse
5 | #################
6 |
7 | *.pydevproject
8 | .project
9 | .metadata
10 | bin/
11 | tmp/
12 | *.tmp
13 | *.bak
14 | *.swp
15 | *~.nib
16 | local.properties
17 | .classpath
18 | .settings/
19 | .loadpath
20 |
21 | # External tool builders
22 | .externalToolBuilders/
23 |
24 | # Locally stored "Eclipse launch configurations"
25 | *.launch
26 |
27 | # CDT-specific
28 | .cproject
29 |
30 | # PDT-specific
31 | .buildpath
32 |
33 |
34 | #################
35 | ## Visual Studio
36 | #################
37 |
38 | ## Ignore Visual Studio temporary files, build results, and
39 | ## files generated by popular Visual Studio add-ons.
40 |
41 | # User-specific files
42 | *.suo
43 | *.user
44 | *.sln.docstates
45 |
46 | # Build results
47 |
48 | [Dd]ebug/
49 | [Rr]elease/
50 | x64/
51 | build/
52 | [Bb]in/
53 | [Oo]bj/
54 |
55 | # MSTest test Results
56 | [Tt]est[Rr]esult*/
57 | [Bb]uild[Ll]og.*
58 |
59 | *_i.c
60 | *_p.c
61 | *.ilk
62 | *.meta
63 | *.obj
64 | *.pch
65 | *.pdb
66 | *.pgc
67 | *.pgd
68 | *.rsp
69 | *.sbr
70 | *.tlb
71 | *.tli
72 | *.tlh
73 | *.tmp
74 | *.tmp_proj
75 | *.log
76 | *.vspscc
77 | *.vssscc
78 | .builds
79 | *.pidb
80 | *.log
81 | *.scc
82 |
83 | # Visual C++ cache files
84 | ipch/
85 | *.aps
86 | *.ncb
87 | *.opensdf
88 | *.sdf
89 | *.cachefile
90 |
91 | # Visual Studio profiler
92 | *.psess
93 | *.vsp
94 | *.vspx
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | *.ncrunch*
111 | .*crunch*.local.xml
112 |
113 | # Installshield output folder
114 | [Ee]xpress/
115 |
116 | # DocProject is a documentation generator add-in
117 | DocProject/buildhelp/
118 | DocProject/Help/*.HxT
119 | DocProject/Help/*.HxC
120 | DocProject/Help/*.hhc
121 | DocProject/Help/*.hhk
122 | DocProject/Help/*.hhp
123 | DocProject/Help/Html2
124 | DocProject/Help/html
125 |
126 | # Click-Once directory
127 | publish/
128 |
129 | # Publish Web Output
130 | *.Publish.xml
131 | *.pubxml
132 |
133 | # NuGet Packages Directory
134 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
135 | #packages/
136 |
137 | # Windows Azure Build Output
138 | csx
139 | *.build.csdef
140 |
141 | # Windows Store app package directory
142 | AppPackages/
143 |
144 | # Others
145 | sql/
146 | *.Cache
147 | ClientBin/
148 | [Ss]tyle[Cc]op.*
149 | ~$*
150 | *~
151 | *.dbmdl
152 | *.[Pp]ublish.xml
153 | *.pfx
154 | *.publishsettings
155 |
156 | # RIA/Silverlight projects
157 | Generated_Code/
158 |
159 | # Backup & report files from converting an old project file to a newer
160 | # Visual Studio version. Backup files are not needed, because we have git ;-)
161 | _UpgradeReport_Files/
162 | Backup*/
163 | UpgradeLog*.XML
164 | UpgradeLog*.htm
165 |
166 | # SQL Server files
167 | App_Data/*.mdf
168 | App_Data/*.ldf
169 |
170 | #############
171 | ## Windows detritus
172 | #############
173 |
174 | # Windows image file caches
175 | Thumbs.db
176 | ehthumbs.db
177 |
178 | # Folder config file
179 | Desktop.ini
180 |
181 | # Recycle Bin used on file shares
182 | $RECYCLE.BIN/
183 |
184 | # Mac crap
185 | .DS_Store
186 |
187 |
188 | #############
189 | ## Python
190 | #############
191 |
192 | *.py[co]
193 |
194 | # Packages
195 | *.egg
196 | *.egg-info
197 | dist/
198 | build/
199 | eggs/
200 | parts/
201 | var/
202 | sdist/
203 | develop-eggs/
204 | .installed.cfg
205 |
206 | # Installer logs
207 | pip-log.txt
208 |
209 | # Unit test / coverage reports
210 | .coverage
211 | .tox
212 |
213 | #Translations
214 | *.mo
215 |
216 | #Mr Developer
217 | .mr.developer.cfg
218 |
--------------------------------------------------------------------------------
/tests/test02_hexagons.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
100 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/webapi.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Web Audio API 学习
8 |
76 |
81 |
82 |
83 | 从audio源获取声音
84 |
85 | audio读取声音
86 |
87 | 频域图模仿
88 |
89 | 圆形声波图
90 |
91 |
92 |
144 |
145 |
--------------------------------------------------------------------------------
/tests/test03_cubes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
125 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/tests/test03_cubes_optimized.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
126 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/css/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v2.1.3 | MIT License | git.io/normalize */
2 |
3 | /* ==========================================================================
4 | HTML5 display definitions
5 | ========================================================================== */
6 |
7 | /**
8 | * Correct `block` display not defined in IE 8/9.
9 | */
10 |
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | hgroup,
19 | main,
20 | nav,
21 | section,
22 | summary {
23 | display: block;
24 | }
25 |
26 | /**
27 | * Correct `inline-block` display not defined in IE 8/9.
28 | */
29 |
30 | audio,
31 | canvas,
32 | video {
33 | display: inline-block;
34 | }
35 |
36 | /**
37 | * Prevent modern browsers from displaying `audio` without controls.
38 | * Remove excess height in iOS 5 devices.
39 | */
40 |
41 | audio:not([controls]) {
42 | display: none;
43 | height: 0;
44 | }
45 |
46 | /**
47 | * Address `[hidden]` styling not present in IE 8/9.
48 | * Hide the `template` element in IE, Safari, and Firefox < 22.
49 | */
50 |
51 | [hidden],
52 | template {
53 | display: none;
54 | }
55 |
56 | /* ==========================================================================
57 | Base
58 | ========================================================================== */
59 |
60 | /**
61 | * 1. Set default font family to sans-serif.
62 | * 2. Prevent iOS text size adjust after orientation change, without disabling
63 | * user zoom.
64 | */
65 |
66 | html {
67 | font-family: sans-serif; /* 1 */
68 | -ms-text-size-adjust: 100%; /* 2 */
69 | -webkit-text-size-adjust: 100%; /* 2 */
70 | }
71 |
72 | /**
73 | * Remove default margin.
74 | */
75 |
76 | body {
77 | margin: 0;
78 | }
79 |
80 | /* ==========================================================================
81 | Links
82 | ========================================================================== */
83 |
84 | /**
85 | * Remove the gray background color from active links in IE 10.
86 | */
87 |
88 | a {
89 | background: transparent;
90 | }
91 |
92 | /**
93 | * Address `outline` inconsistency between Chrome and other browsers.
94 | */
95 |
96 | a:focus {
97 | outline: thin dotted;
98 | }
99 |
100 | /**
101 | * Improve readability when focused and also mouse hovered in all browsers.
102 | */
103 |
104 | a:active,
105 | a:hover {
106 | outline: 0;
107 | }
108 |
109 | /* ==========================================================================
110 | Typography
111 | ========================================================================== */
112 |
113 | /**
114 | * Address variable `h1` font-size and margin within `section` and `article`
115 | * contexts in Firefox 4+, Safari 5, and Chrome.
116 | */
117 |
118 | h1 {
119 | font-size: 2em;
120 | margin: 0.67em 0;
121 | }
122 |
123 | /**
124 | * Address styling not present in IE 8/9, Safari 5, and Chrome.
125 | */
126 |
127 | abbr[title] {
128 | border-bottom: 1px dotted;
129 | }
130 |
131 | /**
132 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
133 | */
134 |
135 | b,
136 | strong {
137 | font-weight: bold;
138 | }
139 |
140 | /**
141 | * Address styling not present in Safari 5 and Chrome.
142 | */
143 |
144 | dfn {
145 | font-style: italic;
146 | }
147 |
148 | /**
149 | * Address differences between Firefox and other browsers.
150 | */
151 |
152 | hr {
153 | -moz-box-sizing: content-box;
154 | box-sizing: content-box;
155 | height: 0;
156 | }
157 |
158 | /**
159 | * Address styling not present in IE 8/9.
160 | */
161 |
162 | mark {
163 | background: #ff0;
164 | color: #000;
165 | }
166 |
167 | /**
168 | * Correct font family set oddly in Safari 5 and Chrome.
169 | */
170 |
171 | code,
172 | kbd,
173 | pre,
174 | samp {
175 | font-family: monospace, serif;
176 | font-size: 1em;
177 | }
178 |
179 | /**
180 | * Improve readability of pre-formatted text in all browsers.
181 | */
182 |
183 | pre {
184 | white-space: pre-wrap;
185 | }
186 |
187 | /**
188 | * Set consistent quote types.
189 | */
190 |
191 | q {
192 | quotes: "\201C" "\201D" "\2018" "\2019";
193 | }
194 |
195 | /**
196 | * Address inconsistent and variable font size in all browsers.
197 | */
198 |
199 | small {
200 | font-size: 80%;
201 | }
202 |
203 | /**
204 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
205 | */
206 |
207 | sub,
208 | sup {
209 | font-size: 75%;
210 | line-height: 0;
211 | position: relative;
212 | vertical-align: baseline;
213 | }
214 |
215 | sup {
216 | top: -0.5em;
217 | }
218 |
219 | sub {
220 | bottom: -0.25em;
221 | }
222 |
223 | /* ==========================================================================
224 | Embedded content
225 | ========================================================================== */
226 |
227 | /**
228 | * Remove border when inside `a` element in IE 8/9.
229 | */
230 |
231 | img {
232 | border: 0;
233 | }
234 |
235 | /**
236 | * Correct overflow displayed oddly in IE 9.
237 | */
238 |
239 | svg:not(:root) {
240 | overflow: hidden;
241 | }
242 |
243 | /* ==========================================================================
244 | Figures
245 | ========================================================================== */
246 |
247 | /**
248 | * Address margin not present in IE 8/9 and Safari 5.
249 | */
250 |
251 | figure {
252 | margin: 0;
253 | }
254 |
255 | /* ==========================================================================
256 | Forms
257 | ========================================================================== */
258 |
259 | /**
260 | * Define consistent border, margin, and padding.
261 | */
262 |
263 | fieldset {
264 | border: 1px solid #c0c0c0;
265 | margin: 0 2px;
266 | padding: 0.35em 0.625em 0.75em;
267 | }
268 |
269 | /**
270 | * 1. Correct `color` not being inherited in IE 8/9.
271 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
272 | */
273 |
274 | legend {
275 | border: 0; /* 1 */
276 | padding: 0; /* 2 */
277 | }
278 |
279 | /**
280 | * 1. Correct font family not being inherited in all browsers.
281 | * 2. Correct font size not being inherited in all browsers.
282 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
283 | */
284 |
285 | button,
286 | input,
287 | select,
288 | textarea {
289 | font-family: inherit; /* 1 */
290 | font-size: 100%; /* 2 */
291 | margin: 0; /* 3 */
292 | }
293 |
294 | /**
295 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
296 | * the UA stylesheet.
297 | */
298 |
299 | button,
300 | input {
301 | line-height: normal;
302 | }
303 |
304 | /**
305 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
306 | * All other form control elements do not inherit `text-transform` values.
307 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
308 | * Correct `select` style inheritance in Firefox 4+ and Opera.
309 | */
310 |
311 | button,
312 | select {
313 | text-transform: none;
314 | }
315 |
316 | /**
317 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
318 | * and `video` controls.
319 | * 2. Correct inability to style clickable `input` types in iOS.
320 | * 3. Improve usability and consistency of cursor style between image-type
321 | * `input` and others.
322 | */
323 |
324 | button,
325 | html input[type="button"], /* 1 */
326 | input[type="reset"],
327 | input[type="submit"] {
328 | -webkit-appearance: button; /* 2 */
329 | cursor: pointer; /* 3 */
330 | }
331 |
332 | /**
333 | * Re-set default cursor for disabled elements.
334 | */
335 |
336 | button[disabled],
337 | html input[disabled] {
338 | cursor: default;
339 | }
340 |
341 | /**
342 | * 1. Address box sizing set to `content-box` in IE 8/9/10.
343 | * 2. Remove excess padding in IE 8/9/10.
344 | */
345 |
346 | input[type="checkbox"],
347 | input[type="radio"] {
348 | box-sizing: border-box; /* 1 */
349 | padding: 0; /* 2 */
350 | }
351 |
352 | /**
353 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
354 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
355 | * (include `-moz` to future-proof).
356 | */
357 |
358 | input[type="search"] {
359 | -webkit-appearance: textfield; /* 1 */
360 | -moz-box-sizing: content-box;
361 | -webkit-box-sizing: content-box; /* 2 */
362 | box-sizing: content-box;
363 | }
364 |
365 | /**
366 | * Remove inner padding and search cancel button in Safari 5 and Chrome
367 | * on OS X.
368 | */
369 |
370 | input[type="search"]::-webkit-search-cancel-button,
371 | input[type="search"]::-webkit-search-decoration {
372 | -webkit-appearance: none;
373 | }
374 |
375 | /**
376 | * Remove inner padding and border in Firefox 4+.
377 | */
378 |
379 | button::-moz-focus-inner,
380 | input::-moz-focus-inner {
381 | border: 0;
382 | padding: 0;
383 | }
384 |
385 | /**
386 | * 1. Remove default vertical scrollbar in IE 8/9.
387 | * 2. Improve readability and alignment in all browsers.
388 | */
389 |
390 | textarea {
391 | overflow: auto; /* 1 */
392 | vertical-align: top; /* 2 */
393 | }
394 |
395 | /* ==========================================================================
396 | Tables
397 | ========================================================================== */
398 |
399 | /**
400 | * Remove most spacing between table cells.
401 | */
402 |
403 | table {
404 | border-collapse: collapse;
405 | border-spacing: 0;
406 | }
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'icomoon';
3 | src: url('fonts/icomoon.eot');
4 | }
5 | @font-face {
6 | font-family: 'icomoon';
7 | src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMghC/LkAAAC8AAAAYGNtYXDmFQCHAAABHAAAAERnYXNwAAAAEAAAAWAAAAAIZ2x5ZkciXAQAAAFoAAAFIGhlYWT+8Lt1AAAGiAAAADZoaGVhA+IB5wAABsAAAAAkaG10eAkAACAAAAbkAAAAGGxvY2EE2gR0AAAG/AAAAA5tYXhwAAwBiQAABwwAAAAgbmFtZUQYtNYAAAcsAAABOXBvc3QAAwAAAAAIaAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAACDmAwHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADAAAAAIAAgAAgAAACDmA//9//8AAAAg5gD//f///+EaAgADAAEAAAAAAAAAAAABAAH//wAPAAEAAAAAAAAAAAACAAA3OQEAAAAABQAAAAACAAHAACgA2QFcAXEBhgAAJSIuAicuATQ2Nz4BMhYXHgMzMj4CNz4BMhYXHgEUBgcOAyMlFB4CFx4DFx4DFx4DFx4DFx4DMx4CMjM6AT4BNzI+Ajc+Azc+Azc+Azc+Azc+AzU0LgInPgM3ND4BNDc0Ni4BNS4DLwEqAgYjIg4CBw4DBw4DBy4DIyIOAgcuAycuAycuAyMiJioBIyoBBiIxDgMHBhQGFBcUHgIVHgMXDgMVFzQ+Ajc+Azc+AzM6AzMWMh4BMxYyFjIzOgE2MjcyPgEyNzoDMzIeAhceAxceAxUcAQ4BBw4DBw4DBw4DBw4DBw4CIiMiBioBIyoCJiMqAS4BJy4DJy4DJy4DJy4DJy4DNTc0PgIzMh4CFRQOAiMiLgI1MzQ+AjMyHgIVFA4CIyIuAjUBAAQHBwcEAgEBAgEEBAQBAgQEBAEBBAQEAgEEBAQBAgEBAgQHBwcE/wABAQMBAgQEBgIDBwcIBQQJCgoFBQwMDAcHDQ4NBwcPDxAICRAPDwcHDQ4OBgcNDAsFBQoKCQUECAgGAwMFBQQBAgMBAQULDwsBAQEBAQEBAQEBAQECAwMCBAEDAwQCAgUFBgMDBwgJBAUKCgsFCRYZHBAQHBkWCQYKCgoFBQgIBwMDBQYFAgMEAwIBAQEBAQIDAwIBAQEBAQEBAQEBAQELDwsFRAQIDAgDBQUGBAMGCAcEBAgIBwQECAkKBQYKCQkEBAgKCgUFCgkJAwQIBwgEBAgHBwMDBgYFAggMCAQCAgEBAwQDAgIFBgYEBAcHBwQDCAkKBQULCQoEBAoLDAYHDAsJBAUJCgoFBgoICAQDBwcHBAQGBgUCAgQDAwECAgEBPAUJCwcHCwkFBQkLBwcLCQXABQkLBwcLCQUFCQsHBwsJBUACBAUEAQQEBAECAQECAgQCAQECBAICAQECAQQEBAEEBQQCmQkSERAIBw4ODAUGCgoJBQQHBwcCAwUEBAICAgMCAQEBAQEBAgMCAgIEBAUDAgcHBwQFCQoKBgUMDg4HCBAREgkRHx0bDAEEBAMDAgUGBgQECAkJBQUKCgsFAQEBAQIBAQIEBAMCBgcHBAMFBQICBQUDBAcHBgIDBAQCAQECAQEBAQULCgoFBQkJCAQEBgYFAgMEAwQBDBsdHxE5CxUUEgkDBQQDAgECAgEBAQEBAQEBAQEBAQICAQIDBAUDCRIUFQsGDAwKBQUJCQcEAwYGBQIDBAMEAQECAgIBAQEBAQEBAQEBAgICAQEEAwQDAgUGBgMEBwkJBQUKDAwGEAoRDQgIDREKChENCAgNEQoKEQ0ICA0RCgoRDQgIDREKAAMAAP/gAgAB4AAUACkALAAAASIOAhUUHgIzMj4CNTQuAiMRIi4CNTQ+AjMyHgIVFA4CIwMXBwEANV1GKChGXTU1XUYoKEZdNStMOCEhOEwrK0w4ISE4TCtAwMAB4ChGXTU1XUYoKEZdNTVdRij+MCE4TCsrTDghIThMKytMOCEBQHBwAAMAAP/gAgAB4AAUABkAJAAAASIOAhUUHgIzMj4CNTQuAiMHMxUjNRMjNTM1IzUzFTMVAQA1XUYoKEZdNTVdRigoRl01IEBAYIAgIGAgAeAoRl01NV1GKChGXTU1XUYoYEBA/sAggCCgIAAAAAMAIAAgAeABgAADAAcACwAAEyEVIRUhFSEVIRUhIAHA/kABwP5AAcD+QAGAYCBgIGAAAAABAAAAAQAAKZhYOF8PPPUACwIAAAAAAM7ovVwAAAAAzui9XAAA/+ACAAHgAAAACAACAAAAAAAAAAEAAAHg/+AAAAIAAAAAAAIAAAEAAAAAAAAAAAAAAAAAAAAGAAAAAAEAAAACAAAAAgAAAAIAAAACAAAgAAAAAAAKAf4CQAJ2ApAAAAABAAAABgGHAAUAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIADgBHAAEAAAAAAAMADgAkAAEAAAAAAAQADgBVAAEAAAAAAAUAFgAOAAEAAAAAAAYABwAyAAEAAAAAAAoAKABjAAMAAQQJAAEADgAAAAMAAQQJAAIADgBHAAMAAQQJAAMADgAkAAMAAQQJAAQADgBVAAMAAQQJAAUAFgAOAAMAAQQJAAYADgA5AAMAAQQJAAoAKABjAGkAYwBvAG0AbwBvAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG0AbwBvAG5pY29tb29uAGkAYwBvAG0AbwBvAG4AUgBlAGcAdQBsAGEAcgBpAGMAbwBtAG8AbwBuAEcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype'),
8 | url(data:application/font-woff;charset=utf-8;base64,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) format('woff');
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 |
13 | [class^="icon-"], [class*=" icon-"] {
14 | font-family: 'icomoon';
15 | speak: none;
16 | font-style: normal;
17 | font-weight: normal;
18 | font-variant: normal;
19 | text-transform: none;
20 | line-height: 1;
21 |
22 | /* Better Font Rendering =========== */
23 | -webkit-font-smoothing: antialiased;
24 | -moz-osx-font-smoothing: grayscale;
25 | }
26 |
27 | .icon-github:before {
28 | content: "\e600";
29 | }
30 | .icon-play:before {
31 | content: "\e601";
32 | }
33 | .icon-info:before {
34 | content: "\e602";
35 | }
36 | .icon-menu:before {
37 | content: "\e603";
38 | }
39 |
40 | * {
41 | padding: 0;
42 | margin: 0;
43 | }
44 | body {
45 | background-color: #000000;
46 | overflow: hidden;
47 | }
48 | a, a:visited {
49 | text-decoration: none;
50 | color: #c6c6c6;
51 | }
52 | a:hover, a:active {
53 | color: #fff;
54 | }
55 | #info {
56 | display: none;
57 | }
58 | #controlPanel {
59 | z-index: 50;
60 | position: absolute;
61 | bottom: 35px;
62 | width: 100%;
63 | color: #999;
64 | max-height: 600px;
65 | -webkit-transition: max-height 0.8s;
66 | -moz-transition: max-height 0.8s;
67 | transition: max-height 0.8s;
68 | overflow: hidden;
69 | }
70 | #controlPanel.hidden {
71 | max-height: 52px;
72 | }
73 | #tab {
74 | padding: 10px 16px 2px 16px;
75 | font-size: 32px;
76 | }
77 | #tab>a {
78 | background-color: rgba(255, 255, 255, 0.15);
79 | padding: 7px 10px 3px 10px;
80 | }
81 | #trackInfoPanel {
82 | float: left;
83 | width: 100%;
84 | overflow: hidden;
85 | max-height: 200px;
86 | -webkit-transition: max-height 0.8s;
87 | -moz-transition: max-height 0.8s;
88 | transition: max-height 0.8s;
89 | background-color: rgba(255, 255, 255, 0.15);
90 | padding-top: 10px;
91 | }
92 | #trackInfoPanel.hidden {
93 | max-height: 0px;
94 | -webkit-transition: max-height 0.2s;
95 | -moz-transition: max-height 0.2s;
96 | transition: max-height 0.2s;
97 | }
98 | #infoImage {
99 | float: left;
100 | padding: 0px 8px 8px 8px;
101 | }
102 | #infoArtist {
103 | font-size: 24px;
104 | }
105 | #infoTrack {
106 | font-size: 30px;
107 | }
108 | #playerControls {
109 | text-align: center;
110 | position: relative;
111 | width: 100%;
112 | float: left;
113 | background-color: rgba(255, 255, 255, 0.15);
114 | padding-top: 10px;
115 | }
116 | #input {
117 | font-size: 20px;
118 | padding: 8px;
119 | margin-bottom: 10px;
120 | margin-left: -30px;
121 | width: 80%;
122 | max-width: 690px;
123 | color: white;
124 | background-color: #EBEBEB;
125 | color: #333;
126 | border: 1px solid #111;
127 | -webkit-border-radius: 3px;
128 | -moz-border-radius: 3px;
129 | border-radius: 3px;
130 | }
131 | #submit {
132 | position: absolute;
133 | color: #fff;
134 | font-size: 32px;
135 | padding: 5px;
136 | background: none;
137 | border: none;
138 | }
139 | #player {
140 | width: 95%;
141 | }
142 | #messageBox {
143 | position: absolute;
144 | top: 30%;
145 | width: 100%;
146 | padding: 20px 0;
147 | text-align: center;
148 | background-color: rgba(255, 255, 255, 0.8);
149 | opacity: 1;
150 | max-height: 500px;
151 | -webkit-transition: opacity 0.5s, max-height 0.5s, padding 0.5s;
152 | -moz-transition: opacity 0.5s, max-height 0.5s, padding 0.5s;
153 | transition: opacity 0.5s, max-height 0.5s, padding 0.5s;
154 | z-index: 500;
155 | overflow: hidden;
156 | }
157 | #messageBox.hidden {
158 | opacity: 0;
159 | max-height: 0;
160 | padding: 0;
161 | }
162 | #messageBox p {
163 | margin-bottom: 10px;
164 | }
165 | #messageBox a:link, #messageBox a:visited {
166 | color: #666;
167 | }
168 | footer {
169 | position: absolute;
170 | bottom: 0;
171 | z-index: 100;
172 | background-color: #111;
173 | width: 100%;
174 | color: #999;
175 | height: 35px;
176 | }
177 | #credit, #github {
178 | display: inline-block;
179 | padding-top: 8px;
180 | padding-right: 10px;
181 | }
182 | #scLogo {
183 | float: right;
184 | }
185 | /* make some txt small for small screens */
186 | @media (max-width: 900px) {
187 | footer {
188 | font-size: 12px;
189 | }
190 | }
191 | @media (max-width: 480px) {
192 | #input {
193 | font-size: 16px;
194 | }
195 | footer {
196 | font-size: 10px;
197 | }
198 | }
--------------------------------------------------------------------------------
/tests/test05_audio_hexagons.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
253 |
262 |
263 |
264 |
265 |
266 |
--------------------------------------------------------------------------------
/tests/test07_audio_hexagons_plus.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
353 |
376 |
377 |
378 |
379 |
380 |
381 |
--------------------------------------------------------------------------------
/tests/test10_audio_hexagons_starfield_2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
391 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
--------------------------------------------------------------------------------
/tests/test08_audio_hexagons_centered.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
413 |
436 |
437 |
438 |
439 |
440 |
441 |
--------------------------------------------------------------------------------
/tests/test12_soundcloud_visualizer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
463 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
--------------------------------------------------------------------------------
/tests/test09_audio_hexagons_starfield.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
436 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
--------------------------------------------------------------------------------
/js/app2.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by Michael on 31/12/13.
3 | */
4 |
5 | /**
6 | * The *AudioSource object creates an analyzer node, sets up a repeating function with setInterval
7 | * which samples the input and turns it into an FFT array. The object has two properties:
8 | * streamData - this is the Uint8Array containing the FFT data
9 | * volume - cumulative value of all the bins of the streaData.
10 | *
11 | * The MicrophoneAudioSource uses the getUserMedia interface to get real-time data from the user's microphone. Not used currently but included for possible future use.
12 | */
13 | var MicrophoneAudioSource = function() {
14 | var self = this;
15 | this.volume = 0;
16 | this.streamData = new Uint8Array(128);
17 | var analyser;
18 |
19 | //全局变量监控声音是否调用,配置别名
20 | var frevolObj={
21 | "Sarah":{"r":105,"g":"","b":255,"volume":0},
22 | "Kathie":{"r":255,"g":"","b":255,"volume":0},
23 | "Christina":{"r":204,"g":"","b":154,"volume":0},
24 | "Frieda":{"r":204,"g":"","b":102,"volume":0},
25 | "Jamie":{"r":255,"g":"","b":51,"volume":0}
26 | // "Sarah":{"r":0,"g":255,"b":153,"volume":0},
27 | // "Kathie":{"r":0,"g":255,"b":30,"volume":0},
28 | // "Christina":{"r":255,"g":255,"b":255,"volume":0},
29 | // "Frieda":{"r":255,"g":255,"b":0,"volume":0},
30 | // "Jamie":{"r":255,"g":51,"b":0,"volume":0}
31 | // "f":{"r":1,"g":"","b":1,"volume":0}
32 |
33 | };
34 |
35 | //触发时间20*25=0.5秒
36 | var num= 10;
37 |
38 | var sampleAudioStream = function() {
39 | //把频域数据填入
40 | analyser.getByteFrequencyData(self.streamData);
41 | num--;
42 |
43 | // calculate an overall volume value
44 | var total = 0;
45 | for(var i in self.streamData) {
46 |
47 | total += self.streamData[i];
48 |
49 | // console.log(self.streamData);
50 | }
51 | self.volume = total;
52 |
53 |
54 | //声音大小
55 | //console.log(self.volume);
56 | //console.log(frequencyVolume(100,120));
57 | //setInterval(yunba("yunbaio",'1','0','0',frequencyVolume(50,70)),1000);
58 | // 声音判断逻辑:
59 | // 变量 frevolArr:保存某一频段的声音大小
60 | // 方法 frequencyVolume;获得某一频段的声音大小(区间0-128,由fftsize确定)
61 | // 方法 yunba :根据别名对某一灯泡传入rgb数据和frevol声音大小
62 | // 方法 closebulb 当所有频率声音大小self.volume小于300时,发送关闭灯泡请求
63 |
64 | frevolObj.Sarah.volume += frequencyVolume(0,20);
65 | frevolObj.Kathie.volume += frequencyVolume(21,40);
66 | frevolObj.Christina.volume += frequencyVolume(41,60);
67 | frevolObj.Frieda.volume += frequencyVolume(61,80);
68 | frevolObj.Jamie.volume += frequencyVolume(81,100);
69 |
70 |
71 | for(var key in frevolObj)
72 | {
73 | var tmp =frevolObj[key].volume;
74 | var r =frevolObj[key].r;
75 | var g =(Number(frevolObj[key].volume)/100).toFixed(0);
76 | var b = frevolObj[key].b;
77 | //调节tmp需要调节checkcv函数
78 | if (tmp>4000&&num<=6) {
79 |
80 | yunba(key,r,g,b,tmp);
81 | console.log(key+"---"+tmp+":rgb("+r+","+g+","+b+")");
82 | frevolObj[key].volume=0;
83 | if (num==0) {
84 | num =25;
85 | };
86 |
87 | }
88 | }
89 |
90 |
91 | };
92 | //某频率段的音量大小
93 | var frequencyVolume=function(start,end){
94 | var fv = 0;
95 | for (var i = start; i 42 ? 1.5 : 2; // increase this value to fade out faster.
266 | this.highlight = 0; // for highlighted stroke effect;
267 | // figure out the x and y coordinates of the center of the polygon based on the
268 | // 60 degree XY axis coordinates passed in
269 | var step = Math.round(Math.cos(Math.PI/6)*tileSize*2);
270 | this.y = Math.round(step * Math.sin(Math.PI/3) * -y );
271 | this.x = Math.round(x * step + y * step/2 );
272 |
273 | // calculate the vertices of the polygon
274 | this.vertices = [];
275 | for (var i = 1; i <= this.sides;i += 1) {
276 | x = this.x + this.tileSize * Math.cos(i * 2 * Math.PI / this.sides + Math.PI/6);
277 | y = this.y + this.tileSize * Math.sin(i * 2 * Math.PI / this.sides + Math.PI/6);
278 | this.vertices.push([x, y]);
279 | }
280 | }
281 | Polygon.prototype.rotateVertices = function() {
282 | // rotate all the vertices to achieve the overall rotational effect
283 | var rotation = fgRotation;
284 | rotation -= audioSource.volume > 10000 ? Math.sin(audioSource.volume/800000) : 0;
285 | for (var i = 0; i <= this.sides-1;i += 1) {
286 | this.vertices[i][0] = this.vertices[i][0] - this.vertices[i][1] * Math.sin(rotation);
287 | this.vertices[i][1] = this.vertices[i][1] + this.vertices[i][0] * Math.sin(rotation);
288 | }
289 | };
290 | var minMental = 0, maxMental = 0;
291 | Polygon.prototype.calculateOffset = function(coords) {
292 | var angle = Math.atan(coords[1]/coords[0]);
293 | var distance = Math.sqrt(Math.pow(coords[0], 2) + Math.pow(coords[1], 2)); // a bit of pythagoras
294 | var mentalFactor = Math.min(Math.max((Math.tan(audioSource.volume/6000) * 0.5), -20), 2); // this factor makes the visualization go crazy wild
295 | /*
296 | // debug
297 | minMental = mentalFactor < minMental ? mentalFactor : minMental;
298 | maxMental = mentalFactor > maxMental ? mentalFactor : maxMental;*/
299 | var offsetFactor = Math.pow(distance/3, 2) * (audioSource.volume/2000000) * (Math.pow(this.high, 1.3)/300) * mentalFactor;
300 | var offsetX = Math.cos(angle) * offsetFactor;
301 | var offsetY = Math.sin(angle) * offsetFactor;
302 | offsetX *= (coords[0] < 0) ? -1 : 1;
303 | offsetY *= (coords[0] < 0) ? -1 : 1;
304 | return [offsetX, offsetY];
305 | };
306 | Polygon.prototype.drawPolygon = function() {
307 | var bucket = Math.ceil(audioSource.streamData.length/tiles.length*this.num);
308 | var val = Math.pow((audioSource.streamData[bucket]/255),2)*255;
309 | //console.log(audioSource.streamData[bucket]/255);
310 | val *= this.num > 42 ? 1.1 : 1;
311 | // establish the value for this tile
312 | if (val > this.high) {
313 | this.high = val;
314 | } else {
315 | this.high -= this.decay;
316 | val = this.high;
317 | }
318 |
319 | // figure out what colour to fill it and then draw the polygon
320 | var r, g, b, a;
321 | if (val > 0) {
322 | this.ctx.beginPath();
323 | var offset = this.calculateOffset(this.vertices[0]);
324 | this.ctx.moveTo(this.vertices[0][0] + offset[0], this.vertices[0][1] + offset[1]);
325 | // draw the polygon
326 | for (var i = 1; i <= this.sides-1;i += 1) {
327 | offset = this.calculateOffset(this.vertices[i]);
328 | this.ctx.lineTo (this.vertices[i][0] + offset[0], this.vertices[i][1] + offset[1]);
329 | }
330 | this.ctx.closePath();
331 |
332 | if (val > 128) {
333 | r = (val-128)*2;
334 | g = ((Math.cos((2*val/128*Math.PI/2)- 4*Math.PI/3)+1)*128);
335 | b = (val-105)*3;
336 | }
337 | else if (val > 175) {
338 | r = (val-128)*2;
339 | g = 255;
340 | b = (val-105)*3;
341 | }
342 | else {
343 | r = ((Math.cos((2*val/128*Math.PI/2))+1)*128);
344 | g = ((Math.cos((2*val/128*Math.PI/2)- 4*Math.PI/3)+1)*128);
345 | b = ((Math.cos((2.4*val/128*Math.PI/2)- 2*Math.PI/3)+1)*128);
346 | }
347 | if (val > 210) {
348 | this.cubed = val; // add the cube effect if it's really loud
349 | }
350 | if (val > 120) {
351 | this.highlight = 100; // add the highlight effect if it's pretty loud
352 | }
353 | // set the alpha
354 | var e = 2.7182;
355 | a = (0.5/(1 + 40 * Math.pow(e, -val/8))) + (0.5/(1 + 40 * Math.pow(e, -val/20)));
356 |
357 | this.ctx.fillStyle = "rgba(" +
358 | Math.round(r) + ", " +
359 | Math.round(g) + ", " +
360 | Math.round(b) + ", " +
361 | a + ")";
362 | // console.log(r);
363 | // console.log(g);
364 | // console.log(b);
365 | this.ctx.fill();
366 | // stroke
367 | if (val > 20) {
368 | var strokeVal = 20;
369 | this.ctx.strokeStyle = "rgba(" + strokeVal + ", " + strokeVal + ", " + strokeVal + ", 0.5)";
370 | this.ctx.lineWidth = 1;
371 | this.ctx.stroke();
372 | }
373 | }
374 | // display the tile number for debug purposes
375 | /*this.ctx.font = "bold 12px sans-serif";
376 | this.ctx.fillStyle = 'grey';
377 | this.ctx.fillText(this.num, this.vertices[0][0], this.vertices[0][1]);*/
378 | };
379 | Polygon.prototype.drawHighlight = function() {
380 | this.ctx.beginPath();
381 | // draw the highlight
382 | var offset = this.calculateOffset(this.vertices[0]);
383 | this.ctx.moveTo(this.vertices[0][0] + offset[0], this.vertices[0][1] + offset[1]);
384 | // draw the polygon
385 | for (var i = 0; i <= this.sides-1;i += 1) {
386 | offset = this.calculateOffset(this.vertices[i]);
387 | this.ctx.lineTo (this.vertices[i][0] + offset[0], this.vertices[i][1] + offset[1]);
388 | }
389 | this.ctx.closePath();
390 | var a = this.highlight/100;
391 | this.ctx.strokeStyle = "rgba(255, 255, 255, " + a + ")";
392 | this.ctx.lineWidth = 1;
393 | this.ctx.stroke();
394 | this.highlight -= 0.5;
395 | };
396 |
397 | var makePolygonArray = function() {
398 | tiles = [];
399 | /**
400 | * Arrange into a grid x, y, with the y axis at 60 degrees to the x, rather than
401 | * the usual 90.
402 | * @type {number}
403 | */
404 | var i = 0; // unique number for each tile
405 | tiles.push(new Polygon(6, 0, 0, tileSize, fgCtx, i)); // the centre tile
406 | i++;
407 | for (var layer = 1; layer < 7; layer++) {
408 | tiles.push(new Polygon(6, 0, layer, tileSize, fgCtx, i)); i++;
409 | tiles.push(new Polygon(6, 0, -layer, tileSize, fgCtx, i)); i++;
410 | for(var x = 1; x < layer; x++) {
411 | tiles.push(new Polygon(6, x, -layer, tileSize, fgCtx, i)); i++;
412 | tiles.push(new Polygon(6, -x, layer, tileSize, fgCtx, i)); i++;
413 | tiles.push(new Polygon(6, x, layer-x, tileSize, fgCtx, i)); i++;
414 | tiles.push(new Polygon(6, -x, -layer+x, tileSize, fgCtx, i)); i++;
415 | }
416 | for(var y = -layer; y <= 0; y++) {
417 | tiles.push(new Polygon(6, layer, y, tileSize, fgCtx, i)); i++;
418 | tiles.push(new Polygon(6, -layer, -y, tileSize, fgCtx, i)); i++;
419 | }
420 | }
421 | };
422 |
423 |
424 | function Star(x, y, starSize, ctx) {
425 | this.x = x;
426 | this.y = y;
427 | this.angle = Math.atan(Math.abs(y)/Math.abs(x));
428 | this.starSize = starSize;
429 | this.ctx = ctx;
430 | this.high = 0;
431 | }
432 | Star.prototype.drawStar = function() {
433 | var distanceFromCentre = Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
434 |
435 | // stars as lines
436 | var brightness = 200 + Math.min(Math.round(this.high * 5), 55);
437 | this.ctx.lineWidth= 0.5 + distanceFromCentre/2000 * Math.max(this.starSize/2, 1);
438 | this.ctx.strokeStyle='rgba(' + brightness + ', ' + brightness + ', ' + brightness + ', 1)';
439 | this.ctx.beginPath();
440 | this.ctx.moveTo(this.x,this.y);
441 | var lengthFactor = 1 + Math.min(Math.pow(distanceFromCentre,2)/30000 * Math.pow(audioSource.volume, 2)/6000000, distanceFromCentre);
442 | var toX = Math.cos(this.angle) * -lengthFactor;
443 | var toY = Math.sin(this.angle) * -lengthFactor;
444 | toX *= this.x > 0 ? 1 : -1;
445 | toY *= this.y > 0 ? 1 : -1;
446 | this.ctx.lineTo(this.x + toX, this.y + toY);
447 | this.ctx.stroke();
448 | this.ctx.closePath();
449 |
450 | // starfield movement coming towards the camera
451 | var speed = lengthFactor/20 * this.starSize;
452 | this.high -= Math.max(this.high - 0.0001, 0);
453 | if (speed > this.high) {
454 | this.high = speed;
455 | }
456 | var dX = Math.cos(this.angle) * this.high;
457 | var dY = Math.sin(this.angle) * this.high;
458 | this.x += this.x > 0 ? dX : -dX;
459 | this.y += this.y > 0 ? dY : -dY;
460 |
461 | var limitY = fgCanvas.height/2 + 500;
462 | var limitX = fgCanvas.width/2 + 500;
463 | if ((this.y > limitY || this.y < -limitY) || (this.x > limitX || this.x < -limitX)) {
464 | // it has gone off the edge so respawn it somewhere near the middle.
465 | this.x = (Math.random() - 0.5) * fgCanvas.width/3;
466 | this.y = (Math.random() - 0.5) * fgCanvas.height/3;
467 | this.angle = Math.atan(Math.abs(this.y)/Math.abs(this.x));
468 | }
469 | };
470 |
471 | var makeStarArray = function() {
472 | var x, y, starSize;
473 | stars = [];
474 | var limit = fgCanvas.width / 15; // how many stars?
475 | for (var i = 0; i < limit; i ++) {
476 | x = (Math.random() - 0.5) * fgCanvas.width;
477 | y = (Math.random() - 0.5) * fgCanvas.height;
478 | starSize = (Math.random()+0.1)*3;
479 | stars.push(new Star(x, y, starSize, sfCtx));
480 | }
481 | };
482 |
483 |
484 | var drawBg = function() {
485 | bgCtx.clearRect(0, 0, bgCanvas.width, bgCanvas.height);
486 | var r, g, b, a;
487 | var val = audioSource.volume/1000;
488 | r = 200 + (Math.sin(val) + 1) * 28;
489 | g = val * 2;
490 | b = val * 8;
491 | a = Math.sin(val+3*Math.PI/2) + 1;
492 | bgCtx.beginPath();
493 | bgCtx.rect(0, 0, bgCanvas.width, bgCanvas.height);
494 | // create radial gradient
495 | var grd = bgCtx.createRadialGradient(bgCanvas.width/2, bgCanvas.height/2, val, bgCanvas.width/2, bgCanvas.height/2, bgCanvas.width-Math.min(Math.pow(val, 2.7), bgCanvas.width - 20));
496 | grd.addColorStop(0, 'rgba(0,0,0,0)');// centre is transparent black
497 | grd.addColorStop(0.8, "rgba(" +
498 | Math.round(r) + ", " +
499 | Math.round(g) + ", " +
500 | Math.round(b) + ", 0.4)"); // edges are reddish
501 |
502 | bgCtx.fillStyle = grd;
503 | bgCtx.fill();
504 |
505 | /*
506 | // debug data
507 | bgCtx.font = "bold 30px sans-serif";
508 | bgCtx.fillStyle = 'grey';
509 | bgCtx.fillText("val: " + val, 30, 30);
510 | bgCtx.fillText("r: " + r , 30, 60);
511 | bgCtx.fillText("g: " + g , 30, 90);
512 | bgCtx.fillText("b: " + b , 30, 120);
513 | bgCtx.fillText("a: " + a , 30, 150);*/
514 | };
515 |
516 | this.resizeCanvas = function() {
517 | if (fgCanvas) {
518 | // resize the foreground canvas
519 | fgCanvas.width = window.innerWidth;
520 | fgCanvas.height = window.innerHeight;
521 | fgCtx.translate(fgCanvas.width/2,fgCanvas.height/2);
522 |
523 | // resize the bg canvas
524 | bgCanvas.width = window.innerWidth;
525 | bgCanvas.height = window.innerHeight;
526 | // resize the starfield canvas
527 | sfCanvas.width = window.innerWidth;
528 | sfCanvas.height = window.innerHeight;
529 | sfCtx.translate(fgCanvas.width/2,fgCanvas.height/2);
530 |
531 | tileSize = fgCanvas.width > fgCanvas.height ? fgCanvas.width / 25 : fgCanvas.height / 25;
532 |
533 |
534 | drawBg();
535 | makePolygonArray();
536 | makeStarArray()
537 | }
538 | };
539 |
540 | var rotateForeground = function() {
541 | tiles.forEach(function(tile) {
542 | tile.rotateVertices();
543 | });
544 | };
545 |
546 | var draw = function() {
547 | fgCtx.clearRect(-fgCanvas.width, -fgCanvas.height, fgCanvas.width*2, fgCanvas.height *2);
548 | sfCtx.clearRect(-fgCanvas.width/2, -fgCanvas.height/2, fgCanvas.width, fgCanvas.height);
549 |
550 | stars.forEach(function(star) {
551 | star.drawStar();
552 | });
553 | tiles.forEach(function(tile) {
554 | tile.drawPolygon();
555 | });
556 | tiles.forEach(function(tile) {
557 | if (tile.highlight > 0) {
558 | tile.drawHighlight();
559 | }
560 | });
561 |
562 | // debug
563 | /* fgCtx.font = "bold 24px sans-serif";
564 | fgCtx.fillStyle = 'grey';
565 | fgCtx.fillText("minMental:" + minMental, 10, 10);
566 | fgCtx.fillText("maxMental:" + maxMental, 10, 40);*/
567 | requestAnimationFrame(draw);
568 | };
569 |
570 | this.init = function(options) {
571 | audioSource = options.audioSource;
572 | var container = document.getElementById(options.containerId);
573 |
574 | // foreground hexagons layer
575 | fgCanvas = document.createElement('canvas');
576 | fgCanvas.setAttribute('style', 'position: absolute; z-index: 10');
577 | fgCtx = fgCanvas.getContext("2d");
578 | container.appendChild(fgCanvas);
579 |
580 | // middle starfield layer
581 | sfCanvas = document.createElement('canvas');
582 | sfCtx = sfCanvas.getContext("2d");
583 | sfCanvas.setAttribute('style', 'position: absolute; z-index: 5');
584 | container.appendChild(sfCanvas);
585 |
586 | // background image layer
587 | bgCanvas = document.createElement('canvas');
588 | bgCtx = bgCanvas.getContext("2d");
589 | container.appendChild(bgCanvas);
590 |
591 | makePolygonArray();
592 | makeStarArray();
593 |
594 | this.resizeCanvas();
595 | draw();
596 |
597 |
598 | setInterval(drawBg, 100);
599 | setInterval(rotateForeground, 20);
600 | // resize the canvas to fill browser window dynamically
601 | window.addEventListener('resize', this.resizeCanvas, false);
602 | };
603 | };
604 |
605 | /**
606 | * Makes a request to the Soundcloud API and returns the JSON data.
607 | */
608 | var SoundcloudLoader = function(player,uiUpdater) {
609 | var self = this;
610 | var client_id = "YOUR_SOUNDCLOUD_CLIENT_ID"; // to get an ID go to http://developers.soundcloud.com/
611 | this.sound = {};
612 | this.streamUrl = "";
613 | this.errorMessage = "";
614 | this.player = player;
615 | this.uiUpdater = uiUpdater;
616 |
617 | /**
618 | * Loads the JSON stream data object from the URL of the track (as given in the location bar of the browser when browsing Soundcloud),
619 | * and on success it calls the callback passed to it (for example, used to then send the stream_url to the audiosource object).
620 | * @param track_url
621 | * @param callback
622 | */
623 | this.loadStream = function(track_url, successCallback, errorCallback) {
624 | SC.initialize({
625 | client_id: client_id
626 | });
627 | SC.get('/resolve', { url: track_url }, function(sound) {
628 | if (sound.errors) {
629 | self.errorMessage = "";
630 | for (var i = 0; i < sound.errors.length; i++) {
631 | self.errorMessage += sound.errors[i].error_message + '
';
632 | }
633 | self.errorMessage += 'Make sure the URL has the correct format: https://soundcloud.com/user/title-of-the-track';
634 | errorCallback();
635 | } else {
636 |
637 | if(sound.kind=="playlist"){
638 | self.sound = sound;
639 | self.streamPlaylistIndex = 0;
640 | self.streamUrl = function(){
641 | return sound.tracks[self.streamPlaylistIndex].stream_url + '?client_id=' + client_id;
642 | };
643 | successCallback();
644 | }else{
645 | self.sound = sound;
646 | self.streamUrl = function(){ return sound.stream_url + '?client_id=' + client_id; };
647 | successCallback();
648 | }
649 | }
650 | });
651 | };
652 |
653 |
654 | this.directStream = function(direction){
655 | if(direction=='toggle'){
656 | if (this.player.paused) {
657 | this.player.play();
658 | } else {
659 | this.player.pause();
660 | }
661 | }
662 | else if(this.sound.kind=="playlist"){
663 | if(direction=='coasting') {
664 | this.streamPlaylistIndex++;
665 | }else if(direction=='forward') {
666 | if(this.streamPlaylistIndex>=this.sound.track_count-1) this.streamPlaylistIndex = 0;
667 | else this.streamPlaylistIndex++;
668 | }else{
669 | if(this.streamPlaylistIndex<=0) this.streamPlaylistIndex = this.sound.track_count-1;
670 | else this.streamPlaylistIndex--;
671 | }
672 | if(this.streamPlaylistIndex>=0 && this.streamPlaylistIndex<=this.sound.track_count-1) {
673 | this.player.setAttribute('src',this.streamUrl());
674 | this.uiUpdater.update(this);
675 | this.player.play();
676 | }
677 | }
678 | }
679 |
680 |
681 | };
682 |
683 | /**
684 | * Class to update the UI when a new sound is loaded
685 | * @constructor
686 | */
687 | var UiUpdater = function() {
688 | var controlPanel = document.getElementById('controlPanel');
689 | var trackInfoPanel = document.getElementById('trackInfoPanel');
690 | var infoImage = document.getElementById('infoImage');
691 | var infoArtist = document.getElementById('infoArtist');
692 | var infoTrack = document.getElementById('infoTrack');
693 | var messageBox = document.getElementById('messageBox');
694 |
695 | this.clearInfoPanel = function() {
696 | // first clear the current contents
697 | infoArtist.innerHTML = "";
698 | infoTrack.innerHTML = "";
699 | trackInfoPanel.className = 'hidden';
700 | };
701 | this.update = function(loader) {
702 | // update the track and artist into in the controlPanel
703 | var artistLink = document.createElement('a');
704 | artistLink.setAttribute('href', loader.sound.user.permalink_url);
705 | artistLink.innerHTML = loader.sound.user.username;
706 | var trackLink = document.createElement('a');
707 | trackLink.setAttribute('href', loader.sound.permalink_url);
708 |
709 | if(loader.sound.kind=="playlist"){
710 | trackLink.innerHTML = "" + loader.sound.tracks[loader.streamPlaylistIndex].title + "
" + ""+loader.sound.title+"
";
711 | }else{
712 | trackLink.innerHTML = loader.sound.title;
713 | }
714 |
715 | var image = loader.sound.artwork_url ? loader.sound.artwork_url : loader.sound.user.avatar_url; // if no track artwork exists, use the user's avatar.
716 | infoImage.setAttribute('src', image);
717 |
718 | infoArtist.innerHTML = '';
719 | infoArtist.appendChild(artistLink);
720 |
721 | infoTrack.innerHTML = '';
722 | infoTrack.appendChild(trackLink);
723 |
724 | // display the track info panel
725 | trackInfoPanel.className = '';
726 |
727 | // add a hash to the URL so it can be shared or saved
728 | var trackToken = loader.sound.permalink_url.substr(22);
729 | window.location = '#' + trackToken;
730 | };
731 | this.toggleControlPanel = function() {
732 | if (controlPanel.className.indexOf('hidden') === 0) {
733 | controlPanel.className = '';
734 | } else {
735 | controlPanel.className = 'hidden';
736 | }
737 | };
738 | this.displayMessage = function(title, message) {
739 | messageBox.innerHTML = ''; // reset the contents
740 |
741 | var titleElement = document.createElement('h3');
742 | titleElement.innerHTML = title;
743 |
744 | var messageElement = document.createElement('p');
745 | messageElement.innerHTML = message;
746 |
747 | var closeButton = document.createElement('a');
748 | closeButton.setAttribute('href', '#');
749 | closeButton.innerHTML = 'close';
750 | closeButton.addEventListener('click', function(e) {
751 | e.preventDefault();
752 | messageBox.className = 'hidden';
753 | });
754 |
755 | messageBox.className = '';
756 | // stick them into the container div
757 | messageBox.appendChild(titleElement);
758 | messageBox.appendChild(messageElement);
759 | messageBox.appendChild(closeButton);
760 | };
761 | };
762 |
763 | window.onload = function init() {
764 |
765 | var visualizer = new Visualizer();
766 | var player = document.getElementById('player');
767 | var uiUpdater = new UiUpdater();
768 | var loader = new SoundcloudLoader(player,uiUpdater);
769 |
770 |
771 | var audioSource = new MicrophoneAudioSource(player);
772 | // var audioSource = new SoundCloudAudioSource(player);
773 | var form = document.getElementById('form');
774 | var loadAndUpdate = function(trackUrl) {
775 | loader.loadStream(trackUrl,
776 | function() {
777 | uiUpdater.clearInfoPanel();
778 | audioSource.playStream(loader.streamUrl());
779 | uiUpdater.update(loader);
780 | setTimeout(uiUpdater.toggleControlPanel, 3000); // auto-hide the control panel
781 | },
782 | function() {
783 | uiUpdater.displayMessage("Error", loader.errorMessage);
784 | });
785 | };
786 |
787 | visualizer.init({
788 | containerId: 'visualizer',
789 | audioSource: audioSource
790 | });
791 |
792 |
793 | uiUpdater.toggleControlPanel();
794 | // on load, check to see if there is a track token in the URL, and if so, load that automatically
795 | if (window.location.hash) {
796 | var trackUrl = 'https://soundcloud.com/' + window.location.hash.substr(1);
797 | loadAndUpdate(trackUrl);
798 | }
799 |
800 | // handle the form submit event to load the new URL
801 | form.addEventListener('submit', function(e) {
802 | e.preventDefault();
803 | var trackUrl = document.getElementById('input').value;
804 | loadAndUpdate(trackUrl);
805 | });
806 | var toggleButton = document.getElementById('toggleButton')
807 | toggleButton.addEventListener('click', function(e) {
808 | e.preventDefault();
809 | uiUpdater.toggleControlPanel();
810 | });
811 | var aboutButton = document.getElementById('credit');
812 | aboutButton.addEventListener('click', function(e) {
813 | e.preventDefault();
814 | var message = document.getElementById('info').innerHTML;
815 | uiUpdater.displayMessage("About", message);
816 | });
817 |
818 | window.addEventListener("keydown", keyControls, false);
819 |
820 | function keyControls(e) {
821 | switch(e.keyCode) {
822 | case 32:
823 | // spacebar pressed
824 | loader.directStream('toggle');
825 | break;
826 | case 37:
827 | // left key pressed
828 | loader.directStream('backward');
829 | break;
830 | case 39:
831 | // right key pressed
832 | loader.directStream('forward');
833 | break;
834 | }
835 | }
836 |
837 |
838 | };
--------------------------------------------------------------------------------