├── libopenmpt.js.mem ├── tunes ├── chipsounds.mod ├── rfchip001.xm ├── cydonian sky.xm └── mysteristerium.mod ├── fonts └── amiga4ever_pro2.ttf ├── README.md ├── config.rb ├── css ├── style.css ├── normalize.min.css ├── screen.css └── normalize.css ├── .gitignore ├── img ├── loop.svg ├── headphones.svg ├── pause.svg └── play.svg ├── sass └── screen.sass ├── index.html └── js └── chiptune2.js /libopenmpt.js.mem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergei-bondarenko/chiptune2.js/master/libopenmpt.js.mem -------------------------------------------------------------------------------- /tunes/chipsounds.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergei-bondarenko/chiptune2.js/master/tunes/chipsounds.mod -------------------------------------------------------------------------------- /tunes/rfchip001.xm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergei-bondarenko/chiptune2.js/master/tunes/rfchip001.xm -------------------------------------------------------------------------------- /tunes/cydonian sky.xm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergei-bondarenko/chiptune2.js/master/tunes/cydonian sky.xm -------------------------------------------------------------------------------- /fonts/amiga4ever_pro2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergei-bondarenko/chiptune2.js/master/fonts/amiga4ever_pro2.ttf -------------------------------------------------------------------------------- /tunes/mysteristerium.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sergei-bondarenko/chiptune2.js/master/tunes/mysteristerium.mod -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Radio.xm 2 | 3 | An endless stream of tracker music. [Listen here](https://cdn.rawgit.com/grez911/chiptune2.js/a64df323/index.html). 4 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | # Require any additional compass plugins here. 2 | 3 | # Set this to the root of your project when deployed: 4 | http_path = "." 5 | css_dir = "./css" 6 | sass_dir = "./sass" 7 | images_dir = "./img" 8 | javascripts_dir = "./js" 9 | 10 | # You can select your preferred output style here (can be overridden via the command line): 11 | # output_style = :expanded or :nested or :compact or :compressed 12 | # output_style = compact 13 | 14 | # To enable relative paths to assets via compass helper functions. Uncomment: 15 | # relative_assets = true 16 | 17 | # To disable debugging comments that display the original location of your selectors. Uncomment: 18 | # line_comments = false 19 | line_comments = false 20 | 21 | preferred_syntax = :sass 22 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: amiga; 3 | src: url("../fonts/amiga4ever_pro2.ttf"); 4 | font-weight: 400; 5 | } 6 | 7 | body 8 | { 9 | background-color: #000000; 10 | color: #00FF00; 11 | font-family: "amiga"; 12 | overflow: hidden; 13 | position: absolute; 14 | width: 100%; 15 | height: 100%; 16 | display: flex; 17 | justify-content: center; /*centers items on the line (the x-axis by default)*/ 18 | align-items: center; /*centers items on the cross-axis (y by default)*/ 19 | } 20 | 21 | a 22 | { 23 | color: #00FF00; 24 | text-decoration: none; 25 | } 26 | 27 | img 28 | { 29 | cursor: pointer; 30 | cursor: hand; 31 | } 32 | 33 | #main 34 | { 35 | width: 500px; 36 | } 37 | 38 | #title 39 | { 40 | text-align: center; 41 | } 42 | 43 | #button 44 | { 45 | margin-top: 15px; 46 | text-align: center; 47 | } 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Windows ### 4 | # Windows image file caches 5 | Thumbs.db 6 | ehthumbs.db 7 | 8 | # Folder config file 9 | Desktop.ini 10 | 11 | # Recycle Bin used on file shares 12 | $RECYCLE.BIN/ 13 | 14 | # Windows Installer files 15 | *.cab 16 | *.msi 17 | *.msm 18 | *.msp 19 | 20 | # Windows shortcuts 21 | *.lnk 22 | 23 | 24 | ### Linux ### 25 | *~ 26 | 27 | # KDE directory preferences 28 | .directory 29 | 30 | 31 | ### OSX ### 32 | .DS_Store 33 | .AppleDouble 34 | .LSOverride 35 | 36 | # Icon must end with two \r 37 | Icon 38 | 39 | 40 | # Thumbnails 41 | ._* 42 | 43 | # Files that might appear on external disk 44 | .Spotlight-V100 45 | .Trashes 46 | 47 | # Directories potentially created on remote AFP share 48 | .AppleDB 49 | .AppleDesktop 50 | Network Trash Folder 51 | Temporary Items 52 | .apdisk 53 | 54 | 55 | ### Sass ### 56 | .sass-cache 57 | *.css.map 58 | 59 | # Webstorm IDE 60 | .idea 61 | -------------------------------------------------------------------------------- /img/loop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /img/headphones.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /img/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /img/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /css/normalize.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.1.0 | MIT License | git.io/normalize */ 2 | article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}html,button,input,select,textarea{font-family:sans-serif}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em;margin:.83em 0}h3{font-size:1.17em;margin:1em 0}h4{font-size:1em;margin:1.33em 0}h5{font-size:.83em;margin:1.67em 0}h6{font-size:.67em;margin:2.33em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:1em 40px}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}p,pre{margin:1em 0}code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:before,q:after{content:'';content:none}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}dl,menu,ol,ul{margin:1em 0}dd{margin:0 0 0 40px}menu,ol,ul{padding:0 0 0 40px}nav ul,nav ol{list-style:none;list-style-image:none}img{border:0;-ms-interpolation-mode:bicubic}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0;white-space:normal;*margin-left:-7px}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*height:13px;*width:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0} -------------------------------------------------------------------------------- /css/screen.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: amiga; 3 | src: url("../fonts/amiga4ever_pro2.ttf"); 4 | font-weight: 400; 5 | } 6 | 7 | html, body { 8 | width: 100%; 9 | height: 100%; 10 | } 11 | 12 | body { 13 | background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff5c00), color-stop(100%, #ff5a00)); 14 | background: -webkit-linear-gradient(#ff5c00, #ff5a00); 15 | background: -moz-linear-gradient(#ff5c00, #ff5a00); 16 | background: -o-linear-gradient(#ff5c00, #ff5a00); 17 | background: linear-gradient(#ff5c00, #ff5a00); 18 | } 19 | 20 | .bg { 21 | width: 100%; 22 | width: calc(100% - 4rem); 23 | height: 100%; 24 | height: calc(100% - 4rem); 25 | position: absolute; 26 | top: 2rem; 27 | left: 2rem; 28 | z-index: -90; 29 | } 30 | .bg.bg1 { 31 | background-image: url("../img/headphones.svg"); 32 | background-repeat: no-repeat; 33 | background-size: contain; 34 | background-position: center; 35 | } 36 | 37 | .banner { 38 | width: 100%; 39 | font-family: amiga, sans-serif; 40 | } 41 | .banner + .banner { 42 | margin-top: 2.4rem; 43 | } 44 | 45 | #headline, #demosong, #metadata, #controls, #choosefile { 46 | text-align: center; 47 | } 48 | 49 | #info { 50 | max-width: calc(100% - 2.6rem); 51 | padding: 1.25rem 1.3rem; 52 | background: rgba(20, 20, 20, 0.86); 53 | color: #f9f9f9; 54 | } 55 | 56 | #headline { 57 | max-width: calc(100% - 2rem); 58 | padding: 1.2rem 1rem; 59 | background: white; 60 | color: #232323; 61 | } 62 | #headline h1 { 63 | margin: 0; 64 | } 65 | @media only screen and (max-width: 45rem) { 66 | #headline h1 { 67 | font-size: 3rem; 68 | } 69 | } 70 | @media only screen and (min-width: 45rem) { 71 | #headline h1 { 72 | font-size: 4.5rem; 73 | } 74 | } 75 | 76 | #demosong { 77 | margin-top: 3rem; 78 | } 79 | #demosong a { 80 | color: #232323; 81 | font-size: 0.7rem; 82 | } 83 | 84 | #metadata { 85 | display: block; 86 | } 87 | 88 | #choosefile input { 89 | padding: 0.5rem; 90 | background: #f7f7f7; 91 | border-radius: 0.35rem; 92 | border: 1px solid rgba(215, 99, 34, 0.78); 93 | } 94 | 95 | #choosefile, #controls { 96 | margin-top: 1.9rem; 97 | } 98 | 99 | #demos { 100 | text-align: center; 101 | } 102 | #demos > div { 103 | display: inline-block; 104 | margin: 0 auto; 105 | text-align: left; 106 | border-radius: 0.35rem; 107 | border: 1px solid rgba(215, 99, 34, 0.78); 108 | padding: 1rem; 109 | background: white; 110 | } 111 | #demos > div h2 { 112 | margin: 0 0 0.5rem 0; 113 | font-size: 1.3rem; 114 | } 115 | #demos > div .song { 116 | display: block; 117 | text-decoration: none; 118 | color: #4c4cbd; 119 | } 120 | #demos > div .song + .song { 121 | margin-top: 0.65rem; 122 | } 123 | #demos > div .song .size { 124 | vertical-align: super; 125 | font-size: 0.6rem; 126 | margin-left: 0.3rem; 127 | } 128 | #demos > div .song .size:before { 129 | content: "("; 130 | } 131 | #demos > div .song .size:after { 132 | content: ")"; 133 | } 134 | 135 | .control { 136 | width: 5rem; 137 | height: 5rem; 138 | border-radius: 2.5rem; 139 | background-color: #505050; 140 | display: inline-block; 141 | box-shadow: 0px 9px 0px #323232, 0px 9px 25px rgba(0, 0, 0, 0.7); 142 | transition: all 0.1s ease; 143 | cursor: pointer; 144 | } 145 | .control:active, .control.pressed { 146 | box-shadow: 0px 3px 0px #323232, 0px 3px 6px rgba(0, 0, 0, 0.7); 147 | } 148 | .control#play { 149 | background-image: url("../img/play.svg"); 150 | background-size: 75%; 151 | background-position: 16px 9px; 152 | background-repeat: no-repeat; 153 | } 154 | .control#pause { 155 | background-image: url("../img/pause.svg"); 156 | background-size: 75%; 157 | background-position: center; 158 | background-repeat: no-repeat; 159 | } 160 | .control#loop { 161 | display: none; 162 | } 163 | .control#loop + label { 164 | background-image: url("../img/loop.svg"); 165 | background-size: 75%; 166 | background-position: center; 167 | background-repeat: no-repeat; 168 | } 169 | .control#loop:checked + label { 170 | box-shadow: 0px 3px 0px #323232, 0px 3px 6px rgba(0, 0, 0, 0.7); 171 | } 172 | -------------------------------------------------------------------------------- /sass/screen.sass: -------------------------------------------------------------------------------- 1 | // chiptune2.js css 2 | 3 | @import compass 4 | 5 | // responsive design 6 | $mobile-width: 45rem 7 | 8 | @mixin respond-to($media) 9 | @if $media == desktop 10 | @media only screen and (min-width: $mobile-width) 11 | @content 12 | @else if $media == mobile 13 | @media only screen and (max-width: $mobile-width) 14 | @content 15 | 16 | // font 17 | @font-face 18 | font-family: amiga 19 | src: url('../fonts/amiga4ever_pro2.ttf') 20 | font-weight: 400 21 | 22 | // our design 23 | html, body 24 | width: 100% 25 | height: 100% 26 | 27 | body 28 | @include background(linear-gradient(#FF5C00, #FF5A00)) 29 | 30 | .bg 31 | width: 100% 32 | width: calc(100% - 4rem) 33 | height: 100% 34 | height: calc(100% - 4rem) 35 | position: absolute 36 | top: 2rem 37 | left: 2rem 38 | z-index: -90 39 | &.bg1 40 | background-image: url('../img/headphones.svg') 41 | background-repeat: no-repeat 42 | background-size: contain 43 | background-position: center 44 | 45 | .banner 46 | width: 100% 47 | font-family: amiga, sans-serif 48 | + .banner 49 | margin-top: 2.4rem 50 | 51 | #headline, #demosong, #metadata, #controls, #choosefile 52 | text-align: center 53 | 54 | #info 55 | max-width: calc(100% - 2.6rem) 56 | padding: 1.25rem 1.3rem 57 | background: rgba(20, 20, 20, 0.86) 58 | color: #f9f9f9 59 | 60 | #headline 61 | max-width: calc(100% - 2rem) 62 | padding: 1.2rem 1rem 63 | background: #fff 64 | color: #232323 65 | h1 66 | margin: 0 67 | @include respond-to(mobile) 68 | font-size: 3rem 69 | @include respond-to(desktop) 70 | font-size: 4.5rem 71 | 72 | #demosong 73 | margin-top: 3rem 74 | a 75 | color: #232323 76 | font-size: 0.7rem 77 | 78 | #metadata 79 | display: block 80 | 81 | #choosefile 82 | input 83 | padding: 0.5rem 84 | background: #f7f7f7 85 | border-radius: 0.35rem 86 | border: 1px solid rgba(215, 99, 34, 0.78) 87 | 88 | #choosefile, #controls 89 | margin-top: 1.9rem 90 | 91 | #demos 92 | text-align: center 93 | > div 94 | display: inline-block 95 | margin: 0 auto 96 | text-align: left 97 | border-radius: 0.35rem 98 | border: 1px solid rgba(215, 99, 34, 0.78) 99 | padding: 1rem 100 | background: #fff 101 | h2 102 | margin: 0 0 0.5rem 0 103 | font-size: 1.3rem 104 | .song 105 | display: block 106 | text-decoration: none 107 | color: #4C4CBD 108 | + .song 109 | margin-top: 0.65rem 110 | .size 111 | vertical-align: super 112 | font-size: 0.6rem 113 | margin-left: 0.3rem 114 | &:before 115 | content: "(" 116 | &:after 117 | content: ")" 118 | 119 | 120 | .control 121 | width: 5rem 122 | height: 5rem 123 | border-radius: 2.5rem 124 | background-color: rgb(80,80,80) 125 | display: inline-block 126 | box-shadow: 0px 9px 0px rgba(50,50,50,1), 0px 9px 25px rgba(0,0,0,.7) 127 | transition: all .1s ease 128 | cursor: pointer 129 | 130 | &:active, &.pressed 131 | box-shadow: 0px 3px 0px rgba(50,50,50,1), 0px 3px 6px rgba(0,0,0,.7) 132 | 133 | &#play 134 | background-image: url('../img/play.svg') 135 | background-size: 75% 136 | background-position: 16px 9px 137 | background-repeat: no-repeat 138 | 139 | &#pause 140 | background-image: url('../img/pause.svg') 141 | background-size: 75% 142 | background-position: center 143 | background-repeat: no-repeat 144 | 145 | &#loop 146 | display: none 147 | + label 148 | background-image: url('../img/loop.svg') 149 | background-size: 75% 150 | background-position: center 151 | background-repeat: no-repeat 152 | &:checked + label 153 | box-shadow: 0px 3px 0px rgba(50,50,50,1), 0px 3px 6px rgba(0,0,0,.7) 154 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Radio.xm 5 | 6 | 7 | 8 | 9 | 10 | 11 | 142 | 143 | 144 |
145 |
Loading...
146 |
147 | 148 | 149 | -------------------------------------------------------------------------------- /js/chiptune2.js: -------------------------------------------------------------------------------- 1 | // audio context 2 | ChiptuneAudioContext = AudioContext || webkitAudioContext; 3 | 4 | // config 5 | function ChiptuneJsConfig(repeatCount, context) { 6 | this.repeatCount = repeatCount; 7 | this.context = context; 8 | } 9 | 10 | // player 11 | function ChiptuneJsPlayer(config) { 12 | this.config = config; 13 | this.context = config.context || new ChiptuneAudioContext; 14 | this.currentPlayingNode = null; 15 | this.handlers = []; 16 | } 17 | 18 | // event handlers section 19 | ChiptuneJsPlayer.prototype.fireEvent = function (eventName, response) { 20 | var handlers = this.handlers; 21 | if (handlers.length) { 22 | handlers.forEach(function (handler) { 23 | if (handler.eventName === eventName) { 24 | handler.handler(response); 25 | } 26 | }) 27 | } 28 | } 29 | 30 | ChiptuneJsPlayer.prototype.addHandler = function (eventName, handler) { 31 | this.handlers.push({eventName: eventName, handler: handler}); 32 | } 33 | 34 | ChiptuneJsPlayer.prototype.onEnded = function (handler) { 35 | this.addHandler('onEnded', handler); 36 | } 37 | 38 | ChiptuneJsPlayer.prototype.onError = function (handler) { 39 | this.addHandler('onError', handler); 40 | } 41 | 42 | // metadata 43 | ChiptuneJsPlayer.prototype.duration = function() { 44 | return Module._openmpt_module_get_duration_seconds(this.currentPlayingNode.modulePtr); 45 | } 46 | 47 | ChiptuneJsPlayer.prototype.metadata = function() { 48 | var data = {}; 49 | var keys = Module.Pointer_stringify(Module._openmpt_module_get_metadata_keys(this.currentPlayingNode.modulePtr)).split(';'); 50 | var keyNameBuffer = 0; 51 | for (var i = 0; i < keys.length; i++) { 52 | keyNameBuffer = Module._malloc(keys[i].length + 1); 53 | Module.writeStringToMemory(keys[i], keyNameBuffer); 54 | data[keys[i]] = Module.Pointer_stringify(Module._openmpt_module_get_metadata(player.currentPlayingNode.modulePtr, keyNameBuffer)); 55 | Module._free(keyNameBuffer); 56 | } 57 | return data; 58 | } 59 | 60 | // playing, etc 61 | ChiptuneJsPlayer.prototype.load = function(input, callback) { 62 | var player = this; 63 | if (input instanceof File) { 64 | var reader = new FileReader(); 65 | reader.onload = function() { 66 | return callback(reader.result); // no error 67 | }.bind(this); 68 | reader.readAsArrayBuffer(input); 69 | } else { 70 | var xhr = new XMLHttpRequest(); 71 | xhr.open('GET', input, true); 72 | xhr.responseType = 'arraybuffer'; 73 | xhr.onload = function(e) { 74 | if (xhr.status === 200) { 75 | return callback(xhr.response); // no error 76 | } else { 77 | player.fireEvent('onError', {type: 'onxhr'}); 78 | } 79 | }.bind(this); 80 | xhr.onerror = function() { 81 | player.fireEvent('onError', {type: 'onxhr'}); 82 | }; 83 | xhr.onabort = function() { 84 | player.fireEvent('onError', {type: 'onxhr'}); 85 | }; 86 | xhr.send(); 87 | } 88 | } 89 | 90 | ChiptuneJsPlayer.prototype.play = function(buffer) { 91 | this.stop(); 92 | var processNode = this.createLibopenmptNode(buffer, this.config); 93 | if (processNode == null) { 94 | return; 95 | } 96 | 97 | // set config options on module 98 | Module._openmpt_module_set_repeat_count(processNode.modulePtr, this.config.repeatCount); 99 | 100 | this.currentPlayingNode = processNode; 101 | processNode.connect(this.context.destination); 102 | } 103 | 104 | ChiptuneJsPlayer.prototype.stop = function() { 105 | if (this.currentPlayingNode != null) { 106 | this.currentPlayingNode.disconnect(); 107 | this.currentPlayingNode.cleanup(); 108 | this.currentPlayingNode = null; 109 | } 110 | } 111 | 112 | ChiptuneJsPlayer.prototype.togglePause = function() { 113 | if (this.currentPlayingNode != null) { 114 | this.currentPlayingNode.togglePause(); 115 | } 116 | } 117 | 118 | ChiptuneJsPlayer.prototype.createLibopenmptNode = function(buffer, config) { 119 | // TODO error checking in this whole function 120 | 121 | var maxFramesPerChunk = 4096; 122 | var processNode = this.context.createScriptProcessor(0, 0, 2); 123 | processNode.config = config; 124 | processNode.player = this; 125 | var byteArray = new Int8Array(buffer); 126 | var ptrToFile = Module._malloc(byteArray.byteLength); 127 | Module.HEAPU8.set(byteArray, ptrToFile); 128 | processNode.modulePtr = Module._openmpt_module_create_from_memory(ptrToFile, byteArray.byteLength, 0, 0, 0); 129 | processNode.paused = false; 130 | processNode.leftBufferPtr = Module._malloc(4 * maxFramesPerChunk); 131 | processNode.rightBufferPtr = Module._malloc(4 * maxFramesPerChunk); 132 | processNode.cleanup = function() { 133 | if (this.modulePtr != 0) { 134 | Module._openmpt_module_destroy(this.modulePtr); 135 | this.modulePtr = 0; 136 | } 137 | if (this.leftBufferPtr != 0) { 138 | Module._free(this.leftBufferPtr); 139 | this.leftBufferPtr = 0; 140 | } 141 | if (this.rightBufferPtr != 0) { 142 | Module._free(this.rightBufferPtr); 143 | this.rightBufferPtr = 0; 144 | } 145 | } 146 | processNode.stop = function() { 147 | this.disconnect(); 148 | this.cleanup(); 149 | } 150 | processNode.pause = function() { 151 | this.paused = true; 152 | } 153 | processNode.unpause = function() { 154 | this.paused = false; 155 | } 156 | processNode.togglePause = function() { 157 | this.paused = !this.paused; 158 | } 159 | processNode.onaudioprocess = function(e) { 160 | var outputL = e.outputBuffer.getChannelData(0); 161 | var outputR = e.outputBuffer.getChannelData(1); 162 | var framesToRender = outputL.length; 163 | if (this.ModulePtr == 0) { 164 | for (var i = 0; i < framesToRender; ++i) { 165 | outputL[i] = 0; 166 | outputR[i] = 0; 167 | } 168 | this.disconnect(); 169 | this.cleanup(); 170 | return; 171 | } 172 | if (this.paused) { 173 | for (var i = 0; i < framesToRender; ++i) { 174 | outputL[i] = 0; 175 | outputR[i] = 0; 176 | } 177 | return; 178 | } 179 | var framesRendered = 0; 180 | var ended = false; 181 | var error = false; 182 | while (framesToRender > 0) { 183 | var framesPerChunk = Math.min(framesToRender, maxFramesPerChunk); 184 | var actualFramesPerChunk = Module._openmpt_module_read_float_stereo(this.modulePtr, this.context.sampleRate, framesPerChunk, this.leftBufferPtr, this.rightBufferPtr); 185 | if (actualFramesPerChunk == 0) { 186 | ended = true; 187 | // modulePtr will be 0 on openmpt: error: openmpt_module_read_float_stereo: ERROR: module * not valid or other openmpt error 188 | error = !this.modulePtr; 189 | } 190 | var rawAudioLeft = Module.HEAPF32.subarray(this.leftBufferPtr / 4, this.leftBufferPtr / 4 + actualFramesPerChunk); 191 | var rawAudioRight = Module.HEAPF32.subarray(this.rightBufferPtr / 4, this.rightBufferPtr / 4 + actualFramesPerChunk); 192 | for (var i = 0; i < actualFramesPerChunk; ++i) { 193 | outputL[framesRendered + i] = rawAudioLeft[i]; 194 | outputR[framesRendered + i] = rawAudioRight[i]; 195 | } 196 | for (var i = actualFramesPerChunk; i < framesPerChunk; ++i) { 197 | outputL[framesRendered + i] = 0; 198 | outputR[framesRendered + i] = 0; 199 | } 200 | framesToRender -= framesPerChunk; 201 | framesRendered += framesPerChunk; 202 | } 203 | if (ended) { 204 | this.disconnect(); 205 | this.cleanup(); 206 | error ? processNode.player.fireEvent('onError', {type: 'openmpt'}) : processNode.player.fireEvent('onEnded'); 207 | } 208 | } 209 | return processNode; 210 | } 211 | 212 | -------------------------------------------------------------------------------- /css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.1.0 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /** 8 | * Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. 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 6/7/8/9 and Firefox 3. 28 | */ 29 | 30 | audio, 31 | canvas, 32 | video { 33 | display: inline-block; 34 | *display: inline; 35 | *zoom: 1; 36 | } 37 | 38 | /** 39 | * Prevent modern browsers from displaying `audio` without controls. 40 | * Remove excess height in iOS 5 devices. 41 | */ 42 | 43 | audio:not([controls]) { 44 | display: none; 45 | height: 0; 46 | } 47 | 48 | /** 49 | * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. 50 | * Known issue: no IE 6 support. 51 | */ 52 | 53 | [hidden] { 54 | display: none; 55 | } 56 | 57 | /* ========================================================================== 58 | Base 59 | ========================================================================== */ 60 | 61 | /** 62 | * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using 63 | * `em` units. 64 | * 2. Prevent iOS text size adjust after orientation change, without disabling 65 | * user zoom. 66 | */ 67 | 68 | html { 69 | font-size: 100%; /* 1 */ 70 | -webkit-text-size-adjust: 100%; /* 2 */ 71 | -ms-text-size-adjust: 100%; /* 2 */ 72 | } 73 | 74 | /** 75 | * Address `font-family` inconsistency between `textarea` and other form 76 | * elements. 77 | */ 78 | 79 | html, 80 | button, 81 | input, 82 | select, 83 | textarea { 84 | font-family: sans-serif; 85 | } 86 | 87 | /** 88 | * Address margins handled incorrectly in IE 6/7. 89 | */ 90 | 91 | body { 92 | margin: 0; 93 | } 94 | 95 | /* ========================================================================== 96 | Links 97 | ========================================================================== */ 98 | 99 | /** 100 | * Address `outline` inconsistency between Chrome and other browsers. 101 | */ 102 | 103 | a:focus { 104 | outline: thin dotted; 105 | } 106 | 107 | /** 108 | * Improve readability when focused and also mouse hovered in all browsers. 109 | */ 110 | 111 | a:active, 112 | a:hover { 113 | outline: 0; 114 | } 115 | 116 | /* ========================================================================== 117 | Typography 118 | ========================================================================== */ 119 | 120 | /** 121 | * Address font sizes and margins set differently in IE 6/7. 122 | * Address font sizes within `section` and `article` in Firefox 4+, Safari 5, 123 | * and Chrome. 124 | */ 125 | 126 | h1 { 127 | font-size: 2em; 128 | margin: 0.67em 0; 129 | } 130 | 131 | h2 { 132 | font-size: 1.5em; 133 | margin: 0.83em 0; 134 | } 135 | 136 | h3 { 137 | font-size: 1.17em; 138 | margin: 1em 0; 139 | } 140 | 141 | h4 { 142 | font-size: 1em; 143 | margin: 1.33em 0; 144 | } 145 | 146 | h5 { 147 | font-size: 0.83em; 148 | margin: 1.67em 0; 149 | } 150 | 151 | h6 { 152 | font-size: 0.67em; 153 | margin: 2.33em 0; 154 | } 155 | 156 | /** 157 | * Address styling not present in IE 7/8/9, Safari 5, and Chrome. 158 | */ 159 | 160 | abbr[title] { 161 | border-bottom: 1px dotted; 162 | } 163 | 164 | /** 165 | * Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. 166 | */ 167 | 168 | b, 169 | strong { 170 | font-weight: bold; 171 | } 172 | 173 | blockquote { 174 | margin: 1em 40px; 175 | } 176 | 177 | /** 178 | * Address styling not present in Safari 5 and Chrome. 179 | */ 180 | 181 | dfn { 182 | font-style: italic; 183 | } 184 | 185 | /** 186 | * Address differences between Firefox and other browsers. 187 | * Known issue: no IE 6/7 normalization. 188 | */ 189 | 190 | hr { 191 | -moz-box-sizing: content-box; 192 | box-sizing: content-box; 193 | height: 0; 194 | } 195 | 196 | /** 197 | * Address styling not present in IE 6/7/8/9. 198 | */ 199 | 200 | mark { 201 | background: #ff0; 202 | color: #000; 203 | } 204 | 205 | /** 206 | * Address margins set differently in IE 6/7. 207 | */ 208 | 209 | p, 210 | pre { 211 | margin: 1em 0; 212 | } 213 | 214 | /** 215 | * Correct font family set oddly in IE 6, Safari 4/5, and Chrome. 216 | */ 217 | 218 | code, 219 | kbd, 220 | pre, 221 | samp { 222 | font-family: monospace, serif; 223 | _font-family: 'courier new', monospace; 224 | font-size: 1em; 225 | } 226 | 227 | /** 228 | * Improve readability of pre-formatted text in all browsers. 229 | */ 230 | 231 | pre { 232 | white-space: pre; 233 | white-space: pre-wrap; 234 | word-wrap: break-word; 235 | } 236 | 237 | /** 238 | * Address CSS quotes not supported in IE 6/7. 239 | */ 240 | 241 | q { 242 | quotes: none; 243 | } 244 | 245 | /** 246 | * Address `quotes` property not supported in Safari 4. 247 | */ 248 | 249 | q:before, 250 | q:after { 251 | content: ''; 252 | content: none; 253 | } 254 | 255 | /** 256 | * Address inconsistent and variable font size in all browsers. 257 | */ 258 | 259 | small { 260 | font-size: 80%; 261 | } 262 | 263 | /** 264 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 265 | */ 266 | 267 | sub, 268 | sup { 269 | font-size: 75%; 270 | line-height: 0; 271 | position: relative; 272 | vertical-align: baseline; 273 | } 274 | 275 | sup { 276 | top: -0.5em; 277 | } 278 | 279 | sub { 280 | bottom: -0.25em; 281 | } 282 | 283 | /* ========================================================================== 284 | Lists 285 | ========================================================================== */ 286 | 287 | /** 288 | * Address margins set differently in IE 6/7. 289 | */ 290 | 291 | dl, 292 | menu, 293 | ol, 294 | ul { 295 | margin: 1em 0; 296 | } 297 | 298 | dd { 299 | margin: 0 0 0 40px; 300 | } 301 | 302 | /** 303 | * Address paddings set differently in IE 6/7. 304 | */ 305 | 306 | menu, 307 | ol, 308 | ul { 309 | padding: 0 0 0 40px; 310 | } 311 | 312 | /** 313 | * Correct list images handled incorrectly in IE 7. 314 | */ 315 | 316 | nav ul, 317 | nav ol { 318 | list-style: none; 319 | list-style-image: none; 320 | } 321 | 322 | /* ========================================================================== 323 | Embedded content 324 | ========================================================================== */ 325 | 326 | /** 327 | * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 328 | * 2. Improve image quality when scaled in IE 7. 329 | */ 330 | 331 | img { 332 | border: 0; /* 1 */ 333 | -ms-interpolation-mode: bicubic; /* 2 */ 334 | } 335 | 336 | /** 337 | * Correct overflow displayed oddly in IE 9. 338 | */ 339 | 340 | svg:not(:root) { 341 | overflow: hidden; 342 | } 343 | 344 | /* ========================================================================== 345 | Figures 346 | ========================================================================== */ 347 | 348 | /** 349 | * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 350 | */ 351 | 352 | figure { 353 | margin: 0; 354 | } 355 | 356 | /* ========================================================================== 357 | Forms 358 | ========================================================================== */ 359 | 360 | /** 361 | * Correct margin displayed oddly in IE 6/7. 362 | */ 363 | 364 | form { 365 | margin: 0; 366 | } 367 | 368 | /** 369 | * Define consistent border, margin, and padding. 370 | */ 371 | 372 | fieldset { 373 | border: 1px solid #c0c0c0; 374 | margin: 0 2px; 375 | padding: 0.35em 0.625em 0.75em; 376 | } 377 | 378 | /** 379 | * 1. Correct color not being inherited in IE 6/7/8/9. 380 | * 2. Correct text not wrapping in Firefox 3. 381 | * 3. Correct alignment displayed oddly in IE 6/7. 382 | */ 383 | 384 | legend { 385 | border: 0; /* 1 */ 386 | padding: 0; 387 | white-space: normal; /* 2 */ 388 | *margin-left: -7px; /* 3 */ 389 | } 390 | 391 | /** 392 | * 1. Correct font size not being inherited in all browsers. 393 | * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, 394 | * and Chrome. 395 | * 3. Improve appearance and consistency in all browsers. 396 | */ 397 | 398 | button, 399 | input, 400 | select, 401 | textarea { 402 | font-size: 100%; /* 1 */ 403 | margin: 0; /* 2 */ 404 | vertical-align: baseline; /* 3 */ 405 | *vertical-align: middle; /* 3 */ 406 | } 407 | 408 | /** 409 | * Address Firefox 3+ setting `line-height` on `input` using `!important` in 410 | * the UA stylesheet. 411 | */ 412 | 413 | button, 414 | input { 415 | line-height: normal; 416 | } 417 | 418 | /** 419 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 420 | * All other form control elements do not inherit `text-transform` values. 421 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. 422 | * Correct `select` style inheritance in Firefox 4+ and Opera. 423 | */ 424 | 425 | button, 426 | select { 427 | text-transform: none; 428 | } 429 | 430 | /** 431 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 432 | * and `video` controls. 433 | * 2. Correct inability to style clickable `input` types in iOS. 434 | * 3. Improve usability and consistency of cursor style between image-type 435 | * `input` and others. 436 | * 4. Remove inner spacing in IE 7 without affecting normal text inputs. 437 | * Known issue: inner spacing remains in IE 6. 438 | */ 439 | 440 | button, 441 | html input[type="button"], /* 1 */ 442 | input[type="reset"], 443 | input[type="submit"] { 444 | -webkit-appearance: button; /* 2 */ 445 | cursor: pointer; /* 3 */ 446 | *overflow: visible; /* 4 */ 447 | } 448 | 449 | /** 450 | * Re-set default cursor for disabled elements. 451 | */ 452 | 453 | button[disabled], 454 | html input[disabled] { 455 | cursor: default; 456 | } 457 | 458 | /** 459 | * 1. Address box sizing set to content-box in IE 8/9. 460 | * 2. Remove excess padding in IE 8/9. 461 | * 3. Remove excess padding in IE 7. 462 | * Known issue: excess padding remains in IE 6. 463 | */ 464 | 465 | input[type="checkbox"], 466 | input[type="radio"] { 467 | box-sizing: border-box; /* 1 */ 468 | padding: 0; /* 2 */ 469 | *height: 13px; /* 3 */ 470 | *width: 13px; /* 3 */ 471 | } 472 | 473 | /** 474 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 475 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 476 | * (include `-moz` to future-proof). 477 | */ 478 | 479 | input[type="search"] { 480 | -webkit-appearance: textfield; /* 1 */ 481 | -moz-box-sizing: content-box; 482 | -webkit-box-sizing: content-box; /* 2 */ 483 | box-sizing: content-box; 484 | } 485 | 486 | /** 487 | * Remove inner padding and search cancel button in Safari 5 and Chrome 488 | * on OS X. 489 | */ 490 | 491 | input[type="search"]::-webkit-search-cancel-button, 492 | input[type="search"]::-webkit-search-decoration { 493 | -webkit-appearance: none; 494 | } 495 | 496 | /** 497 | * Remove inner padding and border in Firefox 3+. 498 | */ 499 | 500 | button::-moz-focus-inner, 501 | input::-moz-focus-inner { 502 | border: 0; 503 | padding: 0; 504 | } 505 | 506 | /** 507 | * 1. Remove default vertical scrollbar in IE 6/7/8/9. 508 | * 2. Improve readability and alignment in all browsers. 509 | */ 510 | 511 | textarea { 512 | overflow: auto; /* 1 */ 513 | vertical-align: top; /* 2 */ 514 | } 515 | 516 | /* ========================================================================== 517 | Tables 518 | ========================================================================== */ 519 | 520 | /** 521 | * Remove most spacing between table cells. 522 | */ 523 | 524 | table { 525 | border-collapse: collapse; 526 | border-spacing: 0; 527 | } 528 | --------------------------------------------------------------------------------