├── .bowerrc ├── .editorconfig ├── .gitignore ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.lock ├── Gruntfile.coffee ├── README.md ├── Rakefile ├── bower.json ├── config.rb ├── data └── examples.yml ├── dist ├── mooog.js ├── mooog.min.js └── mooog.min.js.map ├── examples ├── .gitignore ├── 1.basic-connections.html ├── 2.parameters.html ├── 2.tracks.html ├── 3.envelopes.html ├── 9.browser-support.html ├── basic-connections.html ├── browser-support.html ├── envelopes.html ├── fonts │ ├── aller-bold.eot │ ├── aller-bold.ttf │ ├── aller-bold.woff │ ├── aller-light.eot │ ├── aller-light.ttf │ ├── aller-light.woff │ ├── novecento-bold.eot │ ├── novecento-bold.ttf │ └── novecento-bold.woff ├── index.html ├── javascripts │ ├── all.js │ ├── body-end.js │ ├── highlight │ │ └── highlight.pack.js │ └── modernizr.js ├── parameters.html ├── sound │ ├── balafon1.mp3 │ └── impulse-responses │ │ └── st-andrews-church-ortf-shaped.mp3 ├── stylesheets │ └── all.css └── tracks.html ├── package.json ├── source ├── .bowerrc ├── 1.basic-connections.html.erb ├── 2.parameters.html.erb ├── 2.tracks.html.erb ├── 3.envelopes.html.erb ├── 9.browser-support.html.erb ├── bower.json ├── fonts │ ├── aller-bold.eot │ ├── aller-bold.ttf │ ├── aller-bold.woff │ ├── aller-light.eot │ ├── aller-light.ttf │ ├── aller-light.woff │ ├── novecento-bold.eot │ ├── novecento-bold.ttf │ └── novecento-bold.woff ├── index.html.erb ├── javascripts │ ├── all.js │ ├── body-end.coffee │ ├── highlight │ │ ├── README.md │ │ └── highlight.pack.js │ └── modernizr.js ├── layouts │ ├── layout.erb │ └── toc.erb ├── partials │ ├── _footer.html.erb │ ├── _ga.html.erb │ ├── _range_slider.html.erb │ ├── basic-connections │ │ ├── _example-1.js.html.erb │ │ └── _example-2.js.html.erb │ ├── envelopes │ │ └── _example-1.js.html.erb │ ├── parameters │ │ ├── _example-1.js.html.erb │ │ └── _example-2.js.html.erb │ └── tracks │ │ └── _example-1.js.html.erb ├── sound │ ├── balafon1.mp3 │ └── impulse-responses │ │ ├── radiostatic-shaped.wav │ │ ├── sportscentre-york-ortf-shaped.wav │ │ ├── st-andrews-church-ortf-shaped.mp3 │ │ ├── st-andrews-church-ortf-shaped.wav │ │ ├── st-marys-abbey-ortf-shaped.wav │ │ ├── terrys-typing-ortf-shaped.wav │ │ └── univ-york-stairway-shaped.wav └── stylesheets │ ├── _buttons.scss │ ├── _footer.scss │ ├── _global.scss │ ├── _hljs.scss │ ├── _range-slider.scss │ ├── _typography.scss │ ├── _variables.scss │ └── all.css.scss └── src ├── Mooog.litcoffee ├── MooogAudioNode.litcoffee └── nodes ├── Analyser.litcoffee ├── AudioBufferSource.litcoffee ├── BiquadFilter.litcoffee ├── ChannelMerger.litcoffee ├── ChannelSplitter.litcoffee ├── Convolver.litcoffee ├── Delay.litcoffee ├── DynamicsCompressor.litcoffee ├── Gain.litcoffee ├── MediaElementSource.litcoffee ├── Oscillator.litcoffee ├── Panner.litcoffee ├── ScriptProcessor.litcoffee ├── StereoPanner.litcoffee ├── Track.litcoffee └── Waveshaper.litcoffee /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory" : "source/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 4 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.hbs] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [*.css] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | [*.scss] 29 | indent_style = space 30 | indent_size = 2 31 | 32 | [*.html] 33 | indent_style = space 34 | indent_size = 2 35 | 36 | [*.coffee] 37 | indent_style = space 38 | indent_size = 2 39 | 40 | [*.litcoffee] 41 | indent_style = space 42 | trim_trailing_whitespace = false 43 | indent_size = 2 44 | 45 | [*.rb] 46 | indent_style = space 47 | indent_size = 2 48 | 49 | [*.yml] 50 | indent_style = space 51 | indent_size = 2 52 | 53 | [*.md] 54 | trim_trailing_whitespace = false 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | # Ignore bundler config 8 | /.bundle 9 | 10 | # Ignore the build directory 11 | /build 12 | 13 | # Ignore cache 14 | /.sass-cache 15 | /.cache 16 | 17 | # Ignore .DS_store file 18 | .DS_Store 19 | 20 | #Ignore node stuff 21 | node_modules 22 | 23 | /index.html 24 | 25 | /examples/scratch 26 | /source/scratch 27 | 28 | jasmine_examples 29 | server.rb 30 | 31 | bower_components 32 | 33 | *.wav 34 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | I welcome feedback, bug reports, and code contributions. 4 | 5 | ## Development 6 | 7 | * npm/grunt 8 | 9 | Mooog is written in literate coffeescript, so the developer-oriented documentation is inline. 10 | 11 | The examples directory is built with [https://middlemanapp.com/](middleman). 12 | 13 | 14 | ## Bug reports 15 | 16 | Please make sure you accompany your report with a reduced test case. 17 | 18 | 19 | ## Code contributions 20 | 21 | Make a pull request with a full description of the purpose of the changes. 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # If you do not have OpenSSL installed, update 2 | # the following line to use "http://" instead 3 | source 'https://rubygems.org' 4 | 5 | gem "middleman", "~>3.3.12" 6 | 7 | # Live-reloading plugin 8 | gem "middleman-livereload", "~> 3.1.0" 9 | 10 | # For faster file watcher updates on Windows: 11 | gem "wdm", "~> 0.1.0", :platforms => [:mswin, :mingw] 12 | 13 | # Windows does not come with time zone data 14 | gem "tzinfo-data", platforms: [:mswin, :mingw, :jruby] 15 | 16 | gem "pry", "0.10.1" 17 | 18 | gem 'middleman-gh-pages', :git => 'git@github.com:mattlima/middleman-gh-pages.git' 19 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: git@github.com:mattlima/middleman-gh-pages.git 3 | revision: edbceb826328857b00dc79caeaf875137a3fc488 4 | specs: 5 | middleman-gh-pages (0.0.3) 6 | rake (> 0.9.3) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activesupport (4.1.13) 12 | i18n (~> 0.6, >= 0.6.9) 13 | json (~> 1.7, >= 1.7.7) 14 | minitest (~> 5.1) 15 | thread_safe (~> 0.1) 16 | tzinfo (~> 1.1) 17 | celluloid (0.16.0) 18 | timers (~> 4.0.0) 19 | chunky_png (1.3.4) 20 | coderay (1.1.0) 21 | coffee-script (2.4.1) 22 | coffee-script-source 23 | execjs 24 | coffee-script-source (1.9.1.1) 25 | compass (1.0.3) 26 | chunky_png (~> 1.2) 27 | compass-core (~> 1.0.2) 28 | compass-import-once (~> 1.0.5) 29 | rb-fsevent (>= 0.9.3) 30 | rb-inotify (>= 0.9) 31 | sass (>= 3.3.13, < 3.5) 32 | compass-core (1.0.3) 33 | multi_json (~> 1.0) 34 | sass (>= 3.3.0, < 3.5) 35 | compass-import-once (1.0.5) 36 | sass (>= 3.2, < 3.5) 37 | em-websocket (0.5.1) 38 | eventmachine (>= 0.12.9) 39 | http_parser.rb (~> 0.6.0) 40 | erubis (2.7.0) 41 | eventmachine (1.0.8) 42 | execjs (2.6.0) 43 | ffi (1.9.10) 44 | haml (4.0.7) 45 | tilt 46 | hike (1.2.3) 47 | hitimes (1.2.2) 48 | hooks (0.4.1) 49 | uber (~> 0.0.14) 50 | http_parser.rb (0.6.0) 51 | i18n (0.7.0) 52 | json (1.8.3) 53 | kramdown (1.8.0) 54 | listen (2.10.1) 55 | celluloid (~> 0.16.0) 56 | rb-fsevent (>= 0.9.3) 57 | rb-inotify (>= 0.9) 58 | method_source (0.8.2) 59 | middleman (3.3.12) 60 | coffee-script (~> 2.2) 61 | compass (>= 1.0.0, < 2.0.0) 62 | compass-import-once (= 1.0.5) 63 | execjs (~> 2.0) 64 | haml (>= 4.0.5) 65 | kramdown (~> 1.2) 66 | middleman-core (= 3.3.12) 67 | middleman-sprockets (>= 3.1.2) 68 | sass (>= 3.4.0, < 4.0) 69 | uglifier (~> 2.5) 70 | middleman-core (3.3.12) 71 | activesupport (~> 4.1.0) 72 | bundler (~> 1.1) 73 | erubis 74 | hooks (~> 0.3) 75 | i18n (~> 0.7.0) 76 | listen (>= 2.7.9, < 3.0) 77 | padrino-helpers (~> 0.12.3) 78 | rack (>= 1.4.5, < 2.0) 79 | rack-test (~> 0.6.2) 80 | thor (>= 0.15.2, < 2.0) 81 | tilt (~> 1.4.1, < 2.0) 82 | middleman-livereload (3.1.1) 83 | em-websocket (>= 0.2.0) 84 | middleman-core (>= 3.0.2) 85 | multi_json (~> 1.0) 86 | rack-livereload 87 | middleman-sprockets (3.4.2) 88 | middleman-core (>= 3.3) 89 | sprockets (~> 2.12.1) 90 | sprockets-helpers (~> 1.1.0) 91 | sprockets-sass (~> 1.3.0) 92 | minitest (5.8.0) 93 | multi_json (1.11.2) 94 | padrino-helpers (0.12.5) 95 | i18n (~> 0.6, >= 0.6.7) 96 | padrino-support (= 0.12.5) 97 | tilt (~> 1.4.1) 98 | padrino-support (0.12.5) 99 | activesupport (>= 3.1) 100 | pry (0.10.1) 101 | coderay (~> 1.1.0) 102 | method_source (~> 0.8.1) 103 | slop (~> 3.4) 104 | rack (1.6.4) 105 | rack-livereload (0.3.16) 106 | rack 107 | rack-test (0.6.3) 108 | rack (>= 1.0) 109 | rake (10.4.2) 110 | rb-fsevent (0.9.6) 111 | rb-inotify (0.9.5) 112 | ffi (>= 0.5.0) 113 | sass (3.4.18) 114 | slop (3.6.0) 115 | sprockets (2.12.4) 116 | hike (~> 1.2) 117 | multi_json (~> 1.0) 118 | rack (~> 1.0) 119 | tilt (~> 1.1, != 1.3.0) 120 | sprockets-helpers (1.1.0) 121 | sprockets (~> 2.0) 122 | sprockets-sass (1.3.1) 123 | sprockets (~> 2.0) 124 | tilt (~> 1.1) 125 | thor (0.19.1) 126 | thread_safe (0.3.5) 127 | tilt (1.4.1) 128 | timers (4.0.4) 129 | hitimes 130 | tzinfo (1.2.2) 131 | thread_safe (~> 0.1) 132 | uber (0.0.15) 133 | uglifier (2.7.2) 134 | execjs (>= 0.3.0) 135 | json (>= 1.8.0) 136 | 137 | PLATFORMS 138 | ruby 139 | 140 | DEPENDENCIES 141 | middleman (~> 3.3.12) 142 | middleman-gh-pages! 143 | middleman-livereload (~> 3.1.0) 144 | pry (= 0.10.1) 145 | tzinfo-data 146 | wdm (~> 0.1.0) 147 | -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | 4 | 5 | 6 | module.exports = (grunt, options) -> 7 | pkg = grunt.file.readJSON('package.json') 8 | require('load-grunt-tasks') grunt 9 | 10 | options = 11 | paths: null # UNIMPLEMENTED 12 | 13 | 14 | 15 | 16 | ###### PLUGIN CONFIGURATIONS ###### 17 | grunt.initConfig 18 | options: options 19 | 20 | pkg: pkg 21 | 22 | # grunt-contrib-watch 23 | watch: 24 | js: 25 | files: ['src/**/*.litcoffee','src/**/*.coffee', '!src/index.litcoffee'] 26 | tasks: ['build'] 27 | 28 | clean: 29 | dist: 30 | ['dist/*'] 31 | temp: 32 | ['src/index.litcoffee'] 33 | 34 | coffee: 35 | coffee_to_js: 36 | expand: true 37 | flatten: false 38 | files: 39 | "dist/mooog.js": "src/index.litcoffee" 40 | 41 | concat: 42 | options: 43 | separator: '\n' 44 | dist: 45 | src: ['src/MooogAudioNode.litcoffee', 'src/nodes/*.litcoffee', 'src/Mooog.litcoffee'], 46 | dest: 'src/index.litcoffee', 47 | 48 | 49 | coffeelint: 50 | app: ['src/**/*.litcoffee'] 51 | options: 52 | max_line_length: 53 | "value": 150, 54 | "level": "error", 55 | "limitComments": true 56 | 57 | uglify: { 58 | mooog: { 59 | options: 60 | sourceMap: true 61 | sourceMapName: 'dist/mooog.min.js.map' 62 | mangle: false 63 | files: { 64 | 'dist/mooog.min.js': ['dist/mooog.js'] 65 | } 66 | } 67 | } 68 | 69 | ######### TASK DEFINITIONS ######### 70 | 71 | 72 | # build, watch 73 | grunt.registerTask 'dev', [ 74 | 'build' 75 | 'watch' 76 | ] 77 | # concat and lint 78 | grunt.registerTask 'build', [ 79 | 'coffeelint' 80 | 'concat' 81 | 'coffee' 82 | 'clean:temp' 83 | ] 84 | # build, docs 85 | grunt.registerTask 'prod', [ 86 | 'concat' 87 | 'clean:dist' 88 | 'coffee' 89 | 'clean:temp' 90 | 'uglify' 91 | ] 92 | 93 | 94 | grunt.registerTask 'default', ['dev'] 95 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'middleman-gh-pages' 2 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mooog", 3 | "version": "0.0.8", 4 | "homepage": "https://github.com/mattlima/mooog", 5 | "authors": [ 6 | "Matt Lima " 7 | ], 8 | "description": "Chainable Audiocontext nodes", 9 | "main": "dist/mooog.js", 10 | "license": "MIT", 11 | "private": false, 12 | "moduleType": [ 13 | "globals" 14 | ], 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "test", 20 | "data", 21 | "tests", 22 | "/docco.css", 23 | "Gemfile*", 24 | "Gruntfile*", 25 | "package.json", 26 | "Rakefile", 27 | "README.md", 28 | "CONTRIBUTING.md", 29 | "config.rb", 30 | "server.rb", 31 | "source", 32 | "spec", 33 | "src" 34 | ], 35 | "devDependencies": { 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | require 'pry' 2 | ### 3 | # Compass 4 | ### 5 | 6 | # config.rb 7 | compass_config do |config| 8 | # Require any additional compass plugins here. 9 | config.add_import_path "bower_components/foundation/scss" 10 | 11 | config.output_style = :compact 12 | end 13 | 14 | ### 15 | # Page options, layouts, aliases and proxies 16 | ### 17 | 18 | # Per-page layout changes: 19 | # 20 | # With no layout 21 | # page "/path/to/file.html", :layout => false 22 | # 23 | # With alternative layout 24 | # page "/path/to/file.html", :layout => :otherlayout 25 | # 26 | # A path which all have the same layout 27 | # with_layout :admin do 28 | # page "/admin/*" 29 | # end 30 | 31 | # Proxy pages (https://middlemanapp.com/advanced/dynamic_pages/) 32 | # proxy "/this-page-has-no-template.html", "/template-file.html", :locals => { 33 | # :which_fake_page => "Rendering a fake page with a local variable" } 34 | 35 | c = 0 36 | data.examples.each do |example| 37 | c += 1 38 | proxy "#{example.url}.html", "/#{example.file}.html", locals: example.merge( { ordinal: c, next_example: data.examples[c], prev_example: data.examples[c-2] } ).symbolize_keys 39 | end 40 | 41 | ### 42 | # Helpers 43 | ### 44 | 45 | # Automatic image dimensions on image_tag helper 46 | # activate :automatic_image_sizes 47 | 48 | # Reload the browser automatically whenever files change 49 | configure :development do 50 | activate :livereload 51 | end 52 | 53 | # Methods defined in the helpers block are available in templates 54 | helpers do 55 | def toc_link(target) 56 | data.examples.each do |example| 57 | if( example.url == target || example.name == target ) 58 | return "#{example.url}.html" 59 | end 60 | end 61 | false 62 | end 63 | end 64 | 65 | set :css_dir, 'stylesheets' 66 | 67 | set :js_dir, 'javascripts' 68 | 69 | set :images_dir, 'images' 70 | 71 | set :relative_links, true 72 | 73 | # Build-specific configuration 74 | configure :build do 75 | # For example, change the Compass output style for deployment 76 | activate :minify_css 77 | 78 | ignore 'bower_components/*' 79 | ignore '*.md' 80 | ignore 'bower.json' 81 | ignore 'scratch/*' 82 | 83 | set :build_dir, 'examples' 84 | 85 | # Minify Javascript on build 86 | #activate :minify_javascript 87 | 88 | # Enable cache buster 89 | # activate :asset_hash 90 | 91 | # Use relative URLs 92 | activate :relative_assets 93 | 94 | # Or use a different image path 95 | # set :http_prefix, "/Content/images/" 96 | end 97 | 98 | after_configuration do 99 | @bower_config = JSON.parse(IO.read("#{root}/.bowerrc")) 100 | sprockets.append_path File.join "#{root}", @bower_config["directory"] 101 | end 102 | -------------------------------------------------------------------------------- /data/examples.yml: -------------------------------------------------------------------------------- 1 | - :url: "basic-connections" 2 | :file: "1.basic-connections" 3 | :name: "Basic Connections" 4 | :check_support: true 5 | - :url: "parameters" 6 | :file: "2.parameters" 7 | :name: "Parameters" 8 | :check_support: true 9 | - :url: "tracks" 10 | :file: "2.tracks" 11 | :name: "Tracks" 12 | :check_support: true 13 | - :url: "envelopes" 14 | :file: "3.envelopes" 15 | :name: "Envelopes" 16 | :check_support: true 17 | - :url: "browser-support" 18 | :file: "9.browser-support" 19 | :name: "Browser Support" 20 | :check_support: false 21 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | source/* 3 | *.wav 4 | server.rb 5 | 6 | build/ 7 | -------------------------------------------------------------------------------- /examples/1.basic-connections.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

.

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

Fan-out with connect()

43 | 44 |
45 | 46 |
47 |
48 | 49 |
50 | 51 |
52 |
53 | 54 | 55 | 56 | 57 |
58 | 59 | 60 | 61 |
62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 |
78 | 79 |

The connect method returns the source node, which is useful for fanout. It retains the existing connection of the source node to the AudioDestination, so in this example you'll hear the oscillator as well as the delays. 80 |

81 | 82 |
M = new Mooog();
 83 | M.node( { id: "osc", node_type: "Oscillator", type: "triangle" } )
 84 |     .connect(
 85 |         M.node( { id: "short_delay", node_type: "Delay", delayTime: 0.25 } )
 86 |     )
 87 |     .connect(
 88 |         M.node( { id: "long_delay", node_type: "Delay", delayTime: 0.75 } )
 89 |     )
 90 | 
 91 | $(document)
 92 |     .on("mousedown", ".trigger1", function(){
 93 |         M.node("osc").start();
 94 |     })
 95 |     .on("mouseup", ".trigger1", function(){
 96 |         M.node("osc").stop();
 97 |     })
 98 | 
 99 | 
100 | 
101 | 
102 | 103 | 104 | 105 | 106 | 107 |

Series connections with chain()

108 | 109 |
110 | 111 |
112 |
113 | 114 |
115 | 116 |
117 |
118 | 119 | 120 | 121 | 122 |
123 | 124 | 125 | 126 |
127 | 128 | 129 | 130 | 131 |
132 | 133 | 134 | 135 | 136 | 137 |
138 |
139 | 140 | 141 | 142 |
143 | 144 |

The chain method returns the destination node and automatically disconnects the source from the AudioDestination, which is useful for effects chains. In this example we also use the buffer_source_file parameter of the Convolver to quickly load an impulse response file. The use of chain() to the Convolver means we don't (and can't) hear any dry signal without making additional connections from the previous node. In most cases using the Track object's send would be preferable since it gives us more flexibility. 145 |

146 | 147 |
M = new Mooog();
148 | M.node( { id: "osc2", node_type: "Oscillator", type: "sawtooth" } )
149 |     .chain(
150 |         M.node( { id: "filter", node_type: "BiquadFilter", frequency: 400 } )
151 |     )
152 |     .chain(
153 |         M.node( { id: "pre-reverb", node_type: "Gain", gain: 0.6 } )
154 |     )
155 |     .chain(
156 |         M.node( { id: "reverb", node_type: "Convolver", buffer_source_file: "sound/impulse-responses/st-andrews-church-ortf-shaped.mp3" } )
157 |     )
158 | 
159 | $(document)
160 |     .on("mousedown", ".trigger2", function(){
161 |         M.node("osc2").start();
162 |     })
163 |     .on("mouseup", ".trigger2", function(){
164 |         M.node("osc2").stop();
165 |     })
166 | 
167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 217 | 218 | 219 | 220 |
221 |
222 | 223 | 224 | 225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 | 248 |
249 |
250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /examples/2.parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

.

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

The param() method

43 | 44 |
45 | 46 |
47 |
48 | 49 |
50 | 51 |
52 |
53 | 54 | 55 | 56 | 57 |
58 | 59 | 60 |
61 |
62 |
63 |
64 | 65 |
66 |
67 |
68 | 69 | 70 | 71 | 72 |
73 | 74 | 75 |
76 |
77 | 83 |
84 |
85 |
86 |
87 | 88 |
89 |
90 | 91 | 92 | 93 |
94 | 95 |

The param() method serves as a common interface to all the underlying AudioNode properties, whether scalar, enumerated, or AudioParams. The syntax will be familiar to anyone using jQuery.

96 | 97 |
M = new Mooog();
 98 | M.node( "osc", "Oscillator" )
 99 |     .chain(
100 |         M.node( { node_type: "Gain", gain: 0.5 } ) //don't blow any speakers
101 |     );
102 | 
103 | $(document)
104 |     .on("mousedown", ".trigger1", function(){
105 |         M.node("osc").start();
106 |     })
107 |     .on("mousedown", ".trigger2", function(){
108 |         M.node("osc").stop();
109 |     })
110 |     .on("click", "[data-waveform]", function(e){
111 |         M.node("osc").param( "type", $(this).data("waveform") );
112 |     })
113 |     .on("change.fndtn.slider", "#freq-slider", function(e){
114 |         val = $("#freq-slider").attr('data-slider')
115 |         M.node( "osc" ).param( "frequency", val )
116 |     })
117 |     .on("change.fndtn.slider", "#detune-slider", function(e){
118 |         val = $("#detune-slider").attr('data-slider')
119 |         M.node( "osc" ).param( "detune", val )
120 |     })
121 |     .on("click", ".reset1", function(){
122 |         M.node("osc").param( { type: 'sine', frequency: 440, detune: 0 } );
123 |         $("#detune-slider").foundation('slider', 'set_value', 0);
124 |         $("#freq-slider").foundation('slider', 'set_value', 440);
125 |     })
126 | 
127 | 
128 | 
129 | 130 | 131 | 132 | 133 | 134 |

Scheduling parameter changes

135 | 136 |
137 | 138 |
139 |
140 | 141 |
142 |
143 | 144 |
145 |
146 |
147 |
148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 |
160 |
161 | 162 | 163 | 164 |
165 | 166 |

The scheduling methods of the AudioParam API are all accessible through additional properties of the param() argument. See the 167 | AudioParam docs for more information. By default, 168 | linearRampToValueAtTime and exponentialRampToValueAtTime begin their ramps at the last scheduled change for 169 | the parameter, even if it is in the past. This can lead to confusing behavior where the value jumps abruptly when function is called. 170 | To override this behavior, the from_now argument can be set to true, which will force the ramp to start at the current time. In any case, 171 | you can add cancel: true to the argument, which will run cancelScheduledValues(0), which may help you get more consistent 172 | results if you are setting up parameter changes dynamically (in response to user input, for example.)

173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
AudioParam native callMooog equivalent
AudioParam.setValueAtTime(val, time)param( { AudioParamName: val, at: time } )
AudioParam.linearRampToValueAtTime(val, time)param( { AudioParamName: val, at: time, ramp: "linear", from_now: true } )
AudioParam.exponentialRampToValueAtTime(val, time)param( { AudioParamName: val, at: time, ramp: "expo", from_now: true } )
AudioParam.setValueCurveAtTime(valueArray, time, duration)*param( { AudioParamName: valueArray, at: time, ramp: "curve", duration: duration } )
AudioParam.setTargetAtTime(val, startTime, timeConstant)param( { AudioParamName: val, at: time, ramp: "expo", timeConstant: timeConstant } )
203 | 204 |

*The interpolation between values when using setValueCurveAtTime() was undefined until recently. Chrome 46.0.2490 uses linear interpolation; earlier versions use nearest neighbor (no interpolation), so the transitions you hear may be smooth or abrupt depending on your browser.

205 | 206 | 207 |
M = new Mooog();
208 | M.node({id:"osc2", node_type:"Oscillator" })
209 |     .chain(
210 |         M.node( { node_type: "Gain", gain: 0.5 } )
211 |     );
212 | 
213 | 
214 | $(document)
215 |     .on("mousedown",".trigger3",function(){
216 |         M.node("osc2").start();
217 |     })
218 |     .on("mousedown",".trigger4",function(){
219 |         M.node("osc2").stop();
220 |     })
221 |     .on("mousedown",".freq",function(){
222 |         M.node("osc2").param({frequency: 200});
223 |     })
224 |     .on("mousedown",".param1",function(){
225 |         M.node("osc2").param({frequency: 800, at: 1 });
226 |     })
227 |     .on("mousedown",".param2",function(){
228 |         M.node("osc2").param({frequency: 800, ramp: "linear", at: 2});
229 |     })
230 |     .on("mousedown",".param3",function(){
231 |         M.node("osc2").param({frequency: 800, ramp: "expo", at: 2});
232 |     })
233 |     .on("mousedown",".param4",function(){
234 |         M.node("osc2").param({frequency: [300, 350, 250, 450, 200, 800], ramp: "curve", at: 2, duration: 2});
235 |     })
236 |     .on("mousedown",".param5",function(){
237 |         M.node("osc2").param({frequency: 800, ramp: "expo", at: 2, timeConstant: 2});
238 |     });
239 | 
240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 315 | 316 | 317 | 318 |
319 |
320 | 321 | 322 | 323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 | 346 |
347 |
348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | -------------------------------------------------------------------------------- /examples/2.tracks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

.

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

Creating tracks and sends

43 | 44 |
45 |
46 |
47 | 48 | Track#balafon 49 | 50 |
51 | 52 | 53 |
54 | 55 |
56 |
57 | 58 | 59 | 60 | 61 |
62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | 70 | 71 |
72 | 73 | 74 |
75 | 76 |
77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 | 86 |
87 | 88 | 89 | 90 | 91 |
92 | 93 | 94 |
95 | 96 | 97 |
98 |
99 |
100 |
101 | 102 | Track#delay 103 | 104 | 105 |
106 |
107 | 108 | 109 | 110 | 111 |
112 | 113 | 114 | 115 | 116 |
117 | 118 | 119 | 120 | 121 |
122 | 123 | 124 |
125 | 126 | 127 | 128 |
129 |
130 |
131 |
132 | 133 | Track#reverb 134 | 135 | 136 |
137 |
138 | 139 | 140 | 141 | 142 |
143 | 144 | 145 | 146 | 147 |
148 | 149 | 150 | 151 | 152 |
153 | 154 | 155 |
156 | 157 | 158 | 159 |
160 |
161 |
162 | 163 | 164 |

In addition to connect() and chain(), Mooog provides a Track object which is a special node that includes a pan/gain stage and a send() method that routes audio to another track, pre- or post-fader. The track object takes a string id as its first argument followed by one or more objects to define the nodes of its internal chain.

165 |

In this example, we've set up 3 Track objects: an AudioBuffer to play a sound, and two effects tracks for delay (sent post-fader) and a reverb (sent pre-fader). Since the returns are also Track objects, they have their own gain and pan controls.

166 |

In the code below, the slider change events are omitted for brevity. Track sends are just Gain objects so changing send levels after init is simply: M.track("balafon").send("delay_send").param("gain", new_gain_value);

167 | 168 |
M = new Mooog();
169 | M.track( "balafon",
170 |     { id: "ding", node_type: "AudioBufferSource", buffer_source_file: "sound/balafon1.mp3", loop: true },
171 |     { id: "compressor", node_type: "DynamicsCompressor", threshold: -30, ratio: 50 } //gross compression to demonstrate automatic chaining
172 | );
173 | M.track( "delay",
174 |     { node_type: "Delay", delayTime: 0.76, feedback: 0.2 }
175 | );
176 | M.track( "reverb",
177 |     { node_type: "Convolver", buffer_source_file: "sound/impulse-responses/st-andrews-church-ortf-shaped.mp3" }
178 | );
179 | 
180 | // track.send( id, destination, pre/post, initial_gain )
181 | M.track("balafon").send( 'delay_send', M.track('delay'), 'post', 0.5);
182 | M.track("balafon").send( 'reverb_send', M.track('reverb'), 'pre', 0.25);
183 | 
184 | $(document)
185 |     .on("mousedown", ".ding.start", function(){
186 |         M.node("ding").start();
187 |     })
188 |     .on("mousedown", ".ding.stop", function(){
189 |         M.node("ding").stop();
190 |     })
191 | 
192 | 
193 | 
194 | 
195 | 196 | 197 | 198 | 199 | 200 | 201 | 230 | 231 | 232 | 233 |
234 |
235 | 236 | 237 | 238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 | 261 |
262 |
263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /examples/3.envelopes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

.

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

ADSR

43 | 44 |
45 |
46 |
47 | 48 | Oscillator#saw 49 | 50 |
51 | 52 | 53 |
54 | 55 |
56 |
57 |
58 |

Parameter control envelopes are a fundamental part of electronic sound production. Mooog's adsr() method allows 59 | you to easily apply the traditional A(ttack) D(elay) S(ustain) R(elease) envelope to any AudioParam. You 60 | specify a base value, a peak attack value, a sustain value, and an array of durations for the phases of the envelope.

61 |
    62 |
  • 4 values produce a complete ADSR envelope
  • 63 |
  • 3 values produce a simpler ASR envelope
  • 64 |
  • If you're triggering sounds based on user input then you can give a two-value times array and 65 | only the attack, delay, and sustain portions of the envelope will be produced. You'll then need to use one of 66 | the other ramping functions to terminate the envelope when appropriate – remembering to set from_now: 67 | true or the ramps will be calculated beginning at the previously scheduled event instead of the time 68 | you callparam(). 69 |
  • 70 |
71 | 72 |
73 | 74 |
75 | 76 | 77 | 78 | 79 |
M = new Mooog();
 80 | M.track( "osc",
 81 |     { id: "saw", node_type: "Oscillator", type: "sawtooth" },
 82 |     { id: "filter", node_type: "BiquadFilter", frequency: 300, Q: 30 },
 83 |     { id: "amplitude", node_type: "Gain", gain: 0 }
 84 | );
 85 | M.node('saw').start();
 86 | 
 87 | $(document)
 88 |     .on("mousedown", ".adsr1", function(){
 89 |         M.node("filter").adsr( "frequency", { base: 300, a: 10000, s:2500, times: [0.5, 0.5, 0, 3] } );
 90 |         M.node("amplitude").adsr( "gain", { base: 0, a: 1, s: 0.7, times: [0.1, 0.9, 0, 3] } );
 91 |     })
 92 |     .on("mousedown", ".adsr2", function(){
 93 |         M.node("filter").adsr( "frequency", { base: 300, a: 10000, s:2500, times: [0.5, 0.5] } );
 94 |         M.node("amplitude").adsr( "gain", { base: 0, a: 1, s: 0.7, times: [0.1, 0.9] } );
 95 |     })
 96 |     .on("mouseup", ".adsr2", function(){
 97 |         M.node("filter").param( { frequency: 300, at: 3, ramp: "expo", from_now: true } );
 98 |         M.node("amplitude").param( { gain: 0, at: 3, ramp: "expo", from_now: true } );
 99 |     })
100 | 
101 | 
102 | 
103 | 
104 | 105 | 106 | 107 | 108 | 109 | 110 | 137 | 138 | 139 | 140 |
141 |
142 | 143 | 144 | 145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | 168 |
169 |
170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /examples/9.browser-support.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

.

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

Browser support

43 | 44 |
45 |
46 |

The Web Audio API is not, and never will be, supported on IE (though it is supported on Edge), which 47 | limits its usefulness for general web projects until Edge supplants IE. Even where it is supported, the 48 | API still has not matured (AudioWorkers, for example, are not implemented anywhere yet) so Mooog doesn't 49 | worry too much about cross-browser compatibility issues. It does, however, implement a patch 50 | for the absence of the StereoPannerNode on for the deprecated Audio API (for Safari), since panning is such a 51 | basic audio operation and the Mooog `Track` object relies on it. Ensuring cross-platform 52 | consistency is on the to-do list once the API stabilizes and browser support improves.

53 | 54 |

Mooog.browser_test() returns an object with a set of boolean properties indicating whether your 55 | browser is current in its support of the spec.

56 | 57 | 58 |
    59 |
  • Your browser support the unprefixed AudioContext object.
  • 60 |
  • Your browser support the start and 61 | stop methods instead of noteon and noteoff.
  • 62 |
  • Your browser support the StereoPannerNode
  • 63 |
  • Your browser support the ScriptProcessorNode
  • 64 | 65 | 66 |
67 | 68 |
69 | 70 |
71 | 72 | 73 | 74 | 75 | 76 | 88 | 89 | 90 | 91 |
92 |
93 | 94 | 95 | 96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | 119 |
120 |
121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /examples/basic-connections.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

1. Basic Connections

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

Fan-out with connect()

43 | 44 |
45 | 46 |
47 |
48 | 49 |
50 | 51 |
52 |
53 | 54 | 55 | 56 | 57 |
58 | 59 | 60 | 61 |
62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 |
78 | 79 |

The connect method returns the source node, which is useful for fanout. It retains the existing connection of the source node to the AudioDestination, so in this example you'll hear the oscillator as well as the delays. 80 |

81 | 82 |
M = new Mooog();
 83 | M.node( { id: "osc", node_type: "Oscillator", type: "triangle" } )
 84 |     .connect(
 85 |         M.node( { id: "short_delay", node_type: "Delay", delayTime: 0.25 } )
 86 |     )
 87 |     .connect(
 88 |         M.node( { id: "long_delay", node_type: "Delay", delayTime: 0.75 } )
 89 |     )
 90 | 
 91 | $(document)
 92 |     .on("mousedown", ".trigger1", function(){
 93 |         M.node("osc").start();
 94 |     })
 95 |     .on("mouseup", ".trigger1", function(){
 96 |         M.node("osc").stop();
 97 |     })
 98 | 
 99 | 
100 | 
101 | 
102 | 103 | 104 | 105 | 106 | 107 |

Series connections with chain()

108 | 109 |
110 | 111 |
112 |
113 | 114 |
115 | 116 |
117 |
118 | 119 | 120 | 121 | 122 |
123 | 124 | 125 | 126 |
127 | 128 | 129 | 130 | 131 |
132 | 133 | 134 | 135 | 136 | 137 |
138 |
139 | 140 | 141 | 142 |
143 | 144 |

The chain method returns the destination node and automatically disconnects the source from the AudioDestination, which is useful for effects chains. In this example we also use the buffer_source_file parameter of the Convolver to quickly load an impulse response file. The use of chain() to the Convolver means we don't (and can't) hear any dry signal without making additional connections from the previous node. In most cases using the Track object's send would be preferable since it gives us more flexibility. 145 |

146 | 147 |
M = new Mooog();
148 | M.node( { id: "osc2", node_type: "Oscillator", type: "sawtooth" } )
149 |     .chain(
150 |         M.node( { id: "filter", node_type: "BiquadFilter", frequency: 400 } )
151 |     )
152 |     .chain(
153 |         M.node( { id: "pre-reverb", node_type: "Gain", gain: 0.6 } )
154 |     )
155 |     .chain(
156 |         M.node( { id: "reverb", node_type: "Convolver", buffer_source_file: "sound/impulse-responses/st-andrews-church-ortf-shaped.mp3" } )
157 |     )
158 | 
159 | $(document)
160 |     .on("mousedown", ".trigger2", function(){
161 |         M.node("osc2").start();
162 |     })
163 |     .on("mouseup", ".trigger2", function(){
164 |         M.node("osc2").stop();
165 |     })
166 | 
167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 217 | 218 | 219 | 220 |
221 |
222 | 223 | 224 | 225 |
226 |
227 |
228 | 231 |
232 |
233 |
234 | 249 |
250 |
251 | 252 |
253 |

AudioContext not fully supported

254 |

Your browser doesn't fully support the current AudioContext spec, so these examples may not function.

255 |

For more information, see the section on browser support.

256 |
257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /examples/browser-support.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

5. Browser Support

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

Browser support

43 | 44 |
45 |
46 |

The Web Audio API is not, and never will be, supported on IE (though it is supported on Edge), which 47 | limits its usefulness for general web projects until Edge supplants IE. Even where it is supported, the 48 | API still has not matured (AudioWorkers, for example, are not implemented anywhere yet) so Mooog doesn't 49 | worry too much about cross-browser compatibility issues. It does, however, implement a patch 50 | for the absence of the StereoPannerNode on for the deprecated Audio API (for Safari), since panning is such a 51 | basic audio operation and the Mooog `Track` object relies on it. Ensuring cross-platform 52 | consistency is on the to-do list once the API stabilizes and browser support improves.

53 | 54 |

Mooog.browser_test() returns an object with a set of boolean properties indicating whether your 55 | browser is current in its support of the spec.

56 | 57 | 58 |
    59 |
  • Your browser support the unprefixed AudioContext object.
  • 60 |
  • Your browser support the start and 61 | stop methods instead of noteon and noteoff.
  • 62 |
  • Your browser support the StereoPannerNode
  • 63 |
  • Your browser support the ScriptProcessorNode
  • 64 | 65 | 66 |
67 | 68 |
69 | 70 |
71 | 72 | 73 | 74 | 75 | 76 | 88 | 89 | 90 | 91 |
92 |
93 | 94 | 95 | 96 |
97 | 100 |
101 |
102 |
103 |
104 |
105 | 120 |
121 |
122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /examples/envelopes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

4. Envelopes

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

ADSR

43 | 44 |
45 |
46 |
47 | 48 | Oscillator#saw 49 | 50 |
51 | 52 | 53 |
54 | 55 |
56 |
57 |
58 |

Parameter control envelopes are a fundamental part of electronic sound production. Mooog's adsr() method allows 59 | you to easily apply the traditional A(ttack) D(elay) S(ustain) R(elease) envelope to any AudioParam. You 60 | specify a base value, a peak attack value, a sustain value, and an array of durations for the phases of the envelope.

61 |
    62 |
  • 4 values produce a complete ADSR envelope
  • 63 |
  • 3 values produce a simpler ASR envelope
  • 64 |
  • If you're triggering sounds based on user input then you can give a two-value times array and 65 | only the attack, delay, and sustain portions of the envelope will be produced. You'll then need to use one of 66 | the other ramping functions to terminate the envelope when appropriate – remembering to set from_now: 67 | true or the ramps will be calculated beginning at the previously scheduled event instead of the time 68 | you callparam(). 69 |
  • 70 |
71 | 72 |
73 | 74 |
75 | 76 | 77 | 78 | 79 |
M = new Mooog();
 80 | M.track( "osc",
 81 |     { id: "saw", node_type: "Oscillator", type: "sawtooth" },
 82 |     { id: "filter", node_type: "BiquadFilter", frequency: 300, Q: 30 },
 83 |     { id: "amplitude", node_type: "Gain", gain: 0 }
 84 | );
 85 | M.node('saw').start();
 86 | 
 87 | $(document)
 88 |     .on("mousedown", ".adsr1", function(){
 89 |         M.node("filter").adsr( "frequency", { base: 300, a: 10000, s:2500, times: [0.5, 0.5, 0, 3] } );
 90 |         M.node("amplitude").adsr( "gain", { base: 0, a: 1, s: 0.7, times: [0.1, 0.9, 0, 3] } );
 91 |     })
 92 |     .on("mousedown", ".adsr2", function(){
 93 |         M.node("filter").adsr( "frequency", { base: 300, a: 10000, s:2500, times: [0.5, 0.5] } );
 94 |         M.node("amplitude").adsr( "gain", { base: 0, a: 1, s: 0.7, times: [0.1, 0.9] } );
 95 |     })
 96 |     .on("mouseup", ".adsr2", function(){
 97 |         M.node("filter").param( { frequency: 300, at: 3, ramp: "expo", from_now: true } );
 98 |         M.node("amplitude").param( { gain: 0, at: 3, ramp: "expo", from_now: true } );
 99 |     })
100 | 
101 | 
102 | 
103 | 
104 | 105 | 106 | 107 | 108 | 109 | 110 | 137 | 138 | 139 | 140 |
141 |
142 | 143 | 144 | 145 |
146 | 149 | 152 |
153 |
154 |
155 | 170 |
171 |
172 | 173 |
174 |

AudioContext not fully supported

175 |

Your browser doesn't fully support the current AudioContext spec, so these examples may not function.

176 |

For more information, see the section on browser support.

177 |
178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /examples/fonts/aller-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/fonts/aller-bold.eot -------------------------------------------------------------------------------- /examples/fonts/aller-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/fonts/aller-bold.ttf -------------------------------------------------------------------------------- /examples/fonts/aller-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/fonts/aller-bold.woff -------------------------------------------------------------------------------- /examples/fonts/aller-light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/fonts/aller-light.eot -------------------------------------------------------------------------------- /examples/fonts/aller-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/fonts/aller-light.ttf -------------------------------------------------------------------------------- /examples/fonts/aller-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/fonts/aller-light.woff -------------------------------------------------------------------------------- /examples/fonts/novecento-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/fonts/novecento-bold.eot -------------------------------------------------------------------------------- /examples/fonts/novecento-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/fonts/novecento-bold.ttf -------------------------------------------------------------------------------- /examples/fonts/novecento-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/fonts/novecento-bold.woff -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Mooog Examples | Table of Contents 11 | 12 | 13 | 14 | 15 | 16 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |

Mooog

33 | 34 |

Examples: Table of Contents

35 |
36 |
37 | 38 |
39 |
40 | 41 |

42 | Comprehensive documentation can be found in the README. 43 |

44 | 45 |
    46 | 47 |
  1. Basic Connections
  2. 48 | 49 | 50 |
  3. Parameters
  4. 51 | 52 | 53 |
  5. Tracks
  6. 54 | 55 | 56 |
  7. Envelopes
  8. 57 | 58 | 59 |
  9. Browser Support
  10. 60 | 61 |
62 |
63 |
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /examples/javascripts/body-end.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | $(document).foundation(); 3 | 4 | if (!Mooog.browser_test().all) { 5 | $('#not-supported').foundation('reveal', 'open'); 6 | } 7 | 8 | $('[data-slider]').on('change.fndtn.slider', function(e) { 9 | var $t, d, val; 10 | $t = $(e.target); 11 | d = $t.data(); 12 | val = $t.attr('data-slider'); 13 | switch (d.mooogTargetType) { 14 | case "send": 15 | return M.track(d.mooogNodeTarget).send(d.mooogParamTarget).param("gain", val); 16 | default: 17 | return M.node(d.mooogNodeTarget).param(d.mooogParamTarget, val); 18 | } 19 | }); 20 | 21 | }).call(this); 22 | -------------------------------------------------------------------------------- /examples/javascripts/highlight/highlight.pack.js: -------------------------------------------------------------------------------- 1 | !function(e){"undefined"!=typeof exports?e(exports):(window.hljs=e({}),"function"==typeof define&&define.amd&&define("hljs",[],function(){return window.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&").replace(//gm,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){return/^(no-?highlight|plain|text)$/i.test(e)}function i(e){var n,t,r,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=/\blang(?:uage)?-([\w-]+)\b/i.exec(i))return w(t[1])?t[1]:"no-highlight";for(i=i.split(/\s+/),n=0,r=i.length;r>n;n++)if(w(i[n])||a(i[n]))return i[n]}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3==i.nodeType?a+=i.nodeValue.length:1==i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset"}function u(e){f+=""}function c(e){("start"==e.event?o:u)(e.node)}for(var s=0,f="",l=[];e.length||r.length;){var g=i();if(f+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){l.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g==e&&g.length&&g[0].offset==s);l.reverse().forEach(o)}else"start"==g[0].event?l.push(g[0].node):l.pop(),c(g.splice(0,1)[0])}return f+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):Object.keys(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\b\w+\b/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var f=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=f.length?t(f.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){for(var t=0;t";return i+=e+'">',i+n+o}function p(){if(!L.k)return n(y);var e="",t=0;L.lR.lastIndex=0;for(var r=L.lR.exec(y);r;){e+=n(y.substr(t,r.index-t));var a=g(L,r);a?(B+=a[1],e+=h(a[0],n(r[0]))):e+=n(r[0]),t=L.lR.lastIndex,r=L.lR.exec(y)}return e+n(y.substr(t))}function d(){var e="string"==typeof L.sL;if(e&&!x[L.sL])return n(y);var t=e?f(L.sL,y,!0,M[L.sL]):l(y,L.sL.length?L.sL:void 0);return L.r>0&&(B+=t.r),e&&(M[L.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){return void 0!==L.sL?d():p()}function v(e,t){var r=e.cN?h(e.cN,"",!0):"";e.rB?(k+=r,y=""):e.eB?(k+=n(t)+r,y=""):(k+=r,y=t),L=Object.create(e,{parent:{value:L}})}function m(e,t){if(y+=e,void 0===t)return k+=b(),0;var r=o(t,L);if(r)return k+=b(),v(r,t),r.rB?0:t.length;var a=u(L,t);if(a){var i=L;i.rE||i.eE||(y+=t),k+=b();do L.cN&&(k+=""),B+=L.r,L=L.parent;while(L!=a.parent);return i.eE&&(k+=n(t)),y="",a.starts&&v(a.starts,""),i.rE?0:t.length}if(c(t,L))throw new Error('Illegal lexeme "'+t+'" for mode "'+(L.cN||"")+'"');return y+=t,t.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,L=i||N,M={},k="";for(R=L;R!=N;R=R.parent)R.cN&&(k=h(R.cN,"",!0)+k);var y="",B=0;try{for(var C,j,I=0;;){if(L.t.lastIndex=I,C=L.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}for(m(t.substr(I)),R=L;R.parent;R=R.parent)R.cN&&(k+="");return{r:B,value:k,language:e,top:L}}catch(O){if(-1!=O.message.indexOf("Illegal"))return{r:0,value:n(t)};throw O}}function l(e,t){t=t||E.languages||Object.keys(x);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(w(n)){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function g(e){return E.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,E.tabReplace)})),E.useBR&&(e=e.replace(/\n/g,"
")),e}function h(e,n,t){var r=n?R[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n=i(e);if(!a(n)){var t;E.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):t=e;var r=t.textContent,o=n?f(n,r,!0):l(r),s=u(t);if(s.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=o.value,o.value=c(s,u(p),r)}o.value=g(o.value),e.innerHTML=o.value,e.className=h(e.className,n,o.language),e.result={language:o.language,re:o.r},o.second_best&&(e.second_best={language:o.second_best.language,re:o.second_best.r})}}function d(e){E=o(E,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){R[e]=n})}function N(){return Object.keys(x)}function w(e){return e=e.toLowerCase(),x[e]||x[R[e]]}var E={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},x={},R={};return e.highlight=f,e.highlightAuto=l,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},c:[{cN:"pi",r:10,b:/^\s*['"]use (strict|asm)['"]/},e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE,{cN:"subst",b:"\\$\\{",e:"\\}"}]},e.CLCM,e.CBCM,{cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/\s*[);\]]/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:[e.CLCM,e.CBCM]}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},{bK:"import",e:"[;$]",k:"import from as",c:[e.ASM,e.QSM]},{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]}],i:/#/}}); 2 | -------------------------------------------------------------------------------- /examples/parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

2. Parameters

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

The param() method

43 | 44 |
45 | 46 |
47 |
48 | 49 |
50 | 51 |
52 |
53 | 54 | 55 | 56 | 57 |
58 | 59 | 60 |
61 |
62 |
63 |
64 | 65 |
66 |
67 |
68 | 69 | 70 | 71 | 72 |
73 | 74 | 75 |
76 |
77 | 83 |
84 |
85 |
86 |
87 | 88 |
89 |
90 | 91 | 92 | 93 |
94 | 95 |

The param() method serves as a common interface to all the underlying AudioNode properties, whether scalar, enumerated, or AudioParams. The syntax will be familiar to anyone using jQuery.

96 | 97 |
M = new Mooog();
 98 | M.node( "osc", "Oscillator" )
 99 |     .chain(
100 |         M.node( { node_type: "Gain", gain: 0.5 } ) //don't blow any speakers
101 |     );
102 | 
103 | $(document)
104 |     .on("mousedown", ".trigger1", function(){
105 |         M.node("osc").start();
106 |     })
107 |     .on("mousedown", ".trigger2", function(){
108 |         M.node("osc").stop();
109 |     })
110 |     .on("click", "[data-waveform]", function(e){
111 |         M.node("osc").param( "type", $(this).data("waveform") );
112 |     })
113 |     .on("change.fndtn.slider", "#freq-slider", function(e){
114 |         val = $("#freq-slider").attr('data-slider')
115 |         M.node( "osc" ).param( "frequency", val )
116 |     })
117 |     .on("change.fndtn.slider", "#detune-slider", function(e){
118 |         val = $("#detune-slider").attr('data-slider')
119 |         M.node( "osc" ).param( "detune", val )
120 |     })
121 |     .on("click", ".reset1", function(){
122 |         M.node("osc").param( { type: 'sine', frequency: 440, detune: 0 } );
123 |         $("#detune-slider").foundation('slider', 'set_value', 0);
124 |         $("#freq-slider").foundation('slider', 'set_value', 440);
125 |     })
126 | 
127 | 
128 | 
129 | 130 | 131 | 132 | 133 | 134 |

Scheduling parameter changes

135 | 136 |
137 | 138 |
139 |
140 | 141 |
142 |
143 | 144 |
145 |
146 |
147 |
148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 |
160 |
161 | 162 | 163 | 164 |
165 | 166 |

The scheduling methods of the AudioParam API are all accessible through additional properties of the param() argument. See the 167 | AudioParam docs for more information. By default, 168 | linearRampToValueAtTime and exponentialRampToValueAtTime begin their ramps at the last scheduled change for 169 | the parameter, even if it is in the past. This can lead to confusing behavior where the value jumps abruptly when function is called. 170 | To override this behavior, the from_now argument can be set to true, which will force the ramp to start at the current time. In any case, 171 | you can add cancel: true to the argument, which will run cancelScheduledValues(0), which may help you get more consistent 172 | results if you are setting up parameter changes dynamically (in response to user input, for example.)

173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
AudioParam native callMooog equivalent
AudioParam.setValueAtTime(val, time)param( { AudioParamName: val, at: time } )
AudioParam.linearRampToValueAtTime(val, time)param( { AudioParamName: val, at: time, ramp: "linear", from_now: true } )
AudioParam.exponentialRampToValueAtTime(val, time)param( { AudioParamName: val, at: time, ramp: "expo", from_now: true } )
AudioParam.setValueCurveAtTime(valueArray, time, duration)*param( { AudioParamName: valueArray, at: time, ramp: "curve", duration: duration } )
AudioParam.setTargetAtTime(val, startTime, timeConstant)param( { AudioParamName: val, at: time, ramp: "expo", timeConstant: timeConstant } )
203 | 204 |

*The interpolation between values when using setValueCurveAtTime() was undefined until recently. Chrome 46.0.2490 uses linear interpolation; earlier versions use nearest neighbor (no interpolation), so the transitions you hear may be smooth or abrupt depending on your browser.

205 | 206 | 207 |
M = new Mooog();
208 | M.node({id:"osc2", node_type:"Oscillator" })
209 |     .chain(
210 |         M.node( { node_type: "Gain", gain: 0.5 } )
211 |     );
212 | 
213 | 
214 | $(document)
215 |     .on("mousedown",".trigger3",function(){
216 |         M.node("osc2").start();
217 |     })
218 |     .on("mousedown",".trigger4",function(){
219 |         M.node("osc2").stop();
220 |     })
221 |     .on("mousedown",".freq",function(){
222 |         M.node("osc2").param({frequency: 200});
223 |     })
224 |     .on("mousedown",".param1",function(){
225 |         M.node("osc2").param({frequency: 800, at: 1 });
226 |     })
227 |     .on("mousedown",".param2",function(){
228 |         M.node("osc2").param({frequency: 800, ramp: "linear", at: 2});
229 |     })
230 |     .on("mousedown",".param3",function(){
231 |         M.node("osc2").param({frequency: 800, ramp: "expo", at: 2});
232 |     })
233 |     .on("mousedown",".param4",function(){
234 |         M.node("osc2").param({frequency: [300, 350, 250, 450, 200, 800], ramp: "curve", at: 2, duration: 2});
235 |     })
236 |     .on("mousedown",".param5",function(){
237 |         M.node("osc2").param({frequency: 800, ramp: "expo", at: 2, timeConstant: 2});
238 |     });
239 | 
240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 315 | 316 | 317 | 318 |
319 |
320 | 321 | 322 | 323 | 331 |
332 |
333 | 348 |
349 |
350 | 351 |
352 |

AudioContext not fully supported

353 |

Your browser doesn't fully support the current AudioContext spec, so these examples may not function.

354 |

For more information, see the section on browser support.

355 |
356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | -------------------------------------------------------------------------------- /examples/sound/balafon1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/sound/balafon1.mp3 -------------------------------------------------------------------------------- /examples/sound/impulse-responses/st-andrews-church-ortf-shaped.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/examples/sound/impulse-responses/st-andrews-church-ortf-shaped.mp3 -------------------------------------------------------------------------------- /examples/tracks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | Mooog examples 34 |

3. Tracks

35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 |

Creating tracks and sends

43 | 44 |
45 |
46 |
47 | 48 | Track#balafon 49 | 50 |
51 | 52 | 53 |
54 | 55 |
56 |
57 | 58 | 59 | 60 | 61 |
62 | 63 | 64 | 65 | 66 |
67 | 68 | 69 | 70 | 71 |
72 | 73 | 74 |
75 | 76 |
77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 | 86 |
87 | 88 | 89 | 90 | 91 |
92 | 93 | 94 |
95 | 96 | 97 |
98 |
99 |
100 |
101 | 102 | Track#delay 103 | 104 | 105 |
106 |
107 | 108 | 109 | 110 | 111 |
112 | 113 | 114 | 115 | 116 |
117 | 118 | 119 | 120 | 121 |
122 | 123 | 124 |
125 | 126 | 127 | 128 |
129 |
130 |
131 |
132 | 133 | Track#reverb 134 | 135 | 136 |
137 |
138 | 139 | 140 | 141 | 142 |
143 | 144 | 145 | 146 | 147 |
148 | 149 | 150 | 151 | 152 |
153 | 154 | 155 |
156 | 157 | 158 | 159 |
160 |
161 |
162 | 163 | 164 |

In addition to connect() and chain(), Mooog provides a Track object which is a special node that includes a pan/gain stage and a send() method that routes audio to another track, pre- or post-fader. The track object takes a string id as its first argument followed by one or more objects to define the nodes of its internal chain.

165 |

In this example, we've set up 3 Track objects: an AudioBuffer to play a sound, and two effects tracks for delay (sent post-fader) and a reverb (sent pre-fader). Since the returns are also Track objects, they have their own gain and pan controls.

166 |

In the code below, the slider change events are omitted for brevity. Track sends are just Gain objects so changing send levels after init is simply: M.track("balafon").send("delay_send").param("gain", new_gain_value);

167 | 168 |
M = new Mooog();
169 | M.track( "balafon",
170 |     { id: "ding", node_type: "AudioBufferSource", buffer_source_file: "sound/balafon1.mp3", loop: true },
171 |     { id: "compressor", node_type: "DynamicsCompressor", threshold: -30, ratio: 50 } //gross compression to demonstrate automatic chaining
172 | );
173 | M.track( "delay",
174 |     { node_type: "Delay", delayTime: 0.76, feedback: 0.2 }
175 | );
176 | M.track( "reverb",
177 |     { node_type: "Convolver", buffer_source_file: "sound/impulse-responses/st-andrews-church-ortf-shaped.mp3" }
178 | );
179 | 
180 | // track.send( id, destination, pre/post, initial_gain )
181 | M.track("balafon").send( 'delay_send', M.track('delay'), 'post', 0.5);
182 | M.track("balafon").send( 'reverb_send', M.track('reverb'), 'pre', 0.25);
183 | 
184 | $(document)
185 |     .on("mousedown", ".ding.start", function(){
186 |         M.node("ding").start();
187 |     })
188 |     .on("mousedown", ".ding.stop", function(){
189 |         M.node("ding").stop();
190 |     })
191 | 
192 | 
193 | 
194 | 
195 | 196 | 197 | 198 | 199 | 200 | 201 | 230 | 231 | 232 | 233 |
234 |
235 | 236 | 237 | 238 | 246 |
247 |
248 | 263 |
264 |
265 | 266 |
267 |

AudioContext not fully supported

268 |

Your browser doesn't fully support the current AudioContext spec, so these examples may not function.

269 |

For more information, see the section on browser support.

270 |
271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mooog", 3 | "version": "0.0.1", 4 | "description": "Chainable AudioNode wrapper functions", 5 | "main": "Gruntfile.coffee", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "coffeelint": "^1.10.1", 12 | "grunt": "^0.4.5", 13 | "grunt-coffeelint": "0.0.13", 14 | "grunt-contrib-clean": "~0.5.0", 15 | "grunt-contrib-coffee": "^0.13.0", 16 | "grunt-contrib-concat": "^0.5.1", 17 | "grunt-contrib-uglify": "~0.4.0", 18 | "grunt-contrib-watch": "~0.6.1", 19 | "load-grunt-tasks": "^3.1.0", 20 | "time-grunt": "^1.1.0" 21 | }, 22 | "scripts": { 23 | "test": "echo \"Error: no test specified\" && exit 1" 24 | }, 25 | "author": "Matt Lima ", 26 | "license": "MIT" 27 | } 28 | -------------------------------------------------------------------------------- /source/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory" : "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /source/1.basic-connections.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

Fan-out with connect()

5 | 6 |
7 | 8 |
9 |
10 | 11 |
12 | 13 |
14 | <%= partial "partials/range_slider", locals: { label: "#short_delay.delayTime", units: "secs", start_val: 0.25, node_target: "short_delay", range: [0.05, 2.0], step: 0.05, param_target: "delayTime", jsid: "slider1" } %> 15 | 16 | <%= partial "partials/range_slider", locals: { label: "#long_delay.delayTime", units: "secs", start_val: 1.25, node_target: "long_delay", range: [0.05, 2.0], step: 0.05, param_target: "delayTime", jsid: "slider2" } %> 17 | 18 | 19 | 20 |
21 |
22 | 23 | 24 | 25 |
26 | 27 |

The connect method returns the source node, which is useful for fanout. It retains the existing connection of the source node to the AudioDestination, so in this example you'll hear the oscillator as well as the delays. 28 |

29 | 30 |
<%= partial 'partials/basic-connections/example-1.js', locals: { new_mooog: true }  %>
31 | 32 | 33 | 34 | 35 | 36 |

Series connections with chain()

37 | 38 |
39 | 40 |
41 |
42 | 43 |
44 | 45 |
46 | <%= partial "partials/range_slider", locals: { label: "#filter.frequency", units: "Hz", start_val: 400, node_target: "filter", range: [400, 4000], step: 25, param_target: "frequency", jsid: "slider3" } %> 47 | 48 | <%= partial "partials/range_slider", locals: { label: "#pre-reverb.gain", units: "", start_val: 0.6, node_target: "pre-reverb", range: [0, 1.0], step: 0.05, param_target: "gain", jsid: "slider4" } %> 49 | 50 | 51 | 52 |
53 |
54 | 55 | 56 | 57 |
58 | 59 |

The chain method returns the destination node and automatically disconnects the source from the AudioDestination, which is useful for effects chains. In this example we also use the buffer_source_file parameter of the Convolver to quickly load an impulse response file. The use of chain() to the Convolver means we don't (and can't) hear any dry signal without making additional connections from the previous node. In most cases using the <%= link_to "Track", toc_link('tracks') %> object's send would be preferable since it gives us more flexibility. 60 |

61 | 62 |
<%= partial 'partials/basic-connections/example-2.js', locals: { new_mooog: true }  %>
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 76 | 77 | 78 | 79 |
80 |
81 | -------------------------------------------------------------------------------- /source/2.parameters.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

The param() method

5 | 6 |
7 | 8 |
9 |
10 | 11 |
12 | 13 |
14 | <%= partial "partials/range_slider", locals: { label: "M.node(\"osc\").param( \"frequency\", ", units: ");", start_val: 440, node_target: "osc", range: [200, 800], step: 1, param_target: "frequency", jsid: "freq-slider" } %> 15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 | <%= partial "partials/range_slider", locals: { label: "M.node(\"osc\").param(\"detune\", ", units: ");", start_val: 0, node_target: "osc", range: [-100, 100], step: 1, param_target: "detune", jsid: "detune-slider" } %> 23 |
24 |
25 | 31 |
32 |
33 |
34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 |
42 | 43 |

The param() method serves as a common interface to all the underlying AudioNode properties, whether scalar, enumerated, or AudioParams. The syntax will be familiar to anyone using jQuery.

44 | 45 |
<%= partial "partials/parameters/example-1.js", locals: { new_mooog: true }  %>
46 | 47 | 48 | 49 | 50 | 51 |

Scheduling parameter changes

52 | 53 |
54 | 55 |
56 |
57 | 58 |
59 |
60 | 61 |
62 |
63 |
64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
77 |
78 | 79 | 80 | 81 |
82 | 83 |

The scheduling methods of the AudioParam API are all accessible through additional properties of the param() argument. See the 84 | AudioParam docs for more information. By default, 85 | linearRampToValueAtTime and exponentialRampToValueAtTime begin their ramps at the last scheduled change for 86 | the parameter, even if it is in the past. This can lead to confusing behavior where the value jumps abruptly when function is called. 87 | To override this behavior, the from_now argument can be set to true, which will force the ramp to start at the current time. In any case, 88 | you can add cancel: true to the argument, which will run cancelScheduledValues(0), which may help you get more consistent 89 | results if you are setting up parameter changes dynamically (in response to user input, for example.)

90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
AudioParam native callMooog equivalent
AudioParam.setValueAtTime(val, time)param( { AudioParamName: val, at: time } )
AudioParam.linearRampToValueAtTime(val, time)param( { AudioParamName: val, at: time, ramp: "linear", from_now: true } )
AudioParam.exponentialRampToValueAtTime(val, time)param( { AudioParamName: val, at: time, ramp: "expo", from_now: true } )
AudioParam.setValueCurveAtTime(valueArray, time, duration)*param( { AudioParamName: valueArray, at: time, ramp: "curve", duration: duration } )
AudioParam.setTargetAtTime(val, startTime, timeConstant)param( { AudioParamName: val, at: time, ramp: "expo", timeConstant: timeConstant } )
120 | 121 |

*The interpolation between values when using setValueCurveAtTime() was undefined until recently. Chrome 46.0.2490 uses linear interpolation; earlier versions use nearest neighbor (no interpolation), so the transitions you hear may be smooth or abrupt depending on your browser.

122 | 123 | 124 |
<%= partial "partials/parameters/example-2.js", locals: { new_mooog: true }  %>
125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 138 | 139 | 140 | 141 |
142 |
143 | -------------------------------------------------------------------------------- /source/2.tracks.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

Creating tracks and sends

5 | 6 |
7 |
8 |
9 | 10 | Track#balafon 11 | 12 |
13 | 14 | 15 |
16 | 17 |
18 | <%= partial "partials/range_slider", locals: { label: "Balafon Pan", units: "", start_val: 0, node_target: "balafon", range: [-1, 1], step: 0.05, param_target: "pan", jsid: "balafon-pan" } %> 19 | 20 | 21 | <%= partial "partials/range_slider", locals: { label: "Balafon Gain", units: "", start_val: 0.5, node_target: "balafon", range: [0, 1], step: 0.05, param_target: "gain", jsid: "balafon-gain" } %> 22 |
23 | 24 |
25 | <%= partial "partials/range_slider", locals: { label: "Delay send level", units: "", start_val: 0.5, node_target: "balafon", range: [0, 1], step: 0.05, param_target: "delay_send", target_type: "send", jsid: "balafon-delay-send" } %> 26 | 27 | <%= partial "partials/range_slider", locals: { label: "Reverb send level", units: "", start_val: 0.25, node_target: "balafon", range: [0, 1], step: 0.05, param_target: "reverb_send", target_type: "send", jsid: "balafon-reverb-send" } %> 28 |
29 | 30 | 31 |
32 |
33 |
34 |
35 | 36 | Track#delay 37 | 38 | 39 |
40 | <%= partial "partials/range_slider", locals: { label: "Delay Pan", units: "", start_val: 0, node_target: "delay", range: [-1, 1], step: 0.05, param_target: "pan", jsid: "delay-pan" } %> 41 | 42 | 43 | <%= partial "partials/range_slider", locals: { label: "Delay Gain", units: "", start_val: 0.5, node_target: "delay", range: [0, 1], step: 0.05, param_target: "gain", jsid: "delay-gain" } %> 44 |
45 | 46 | 47 | 48 |
49 |
50 |
51 |
52 | 53 | Track#reverb 54 | 55 | 56 |
57 | <%= partial "partials/range_slider", locals: { label: "Reverb Pan", units: "", start_val: 0, node_target: "reverb", range: [-1, 1], step: 0.05, param_target: "pan", jsid: "reverb-pan" } %> 58 | 59 | 60 | <%= partial "partials/range_slider", locals: { label: "Reverb Gain", units: "", start_val: 0.5, node_target: "reverb", range: [0, 1], step: 0.05, param_target: "gain", jsid: "reverb-gain" } %> 61 |
62 | 63 | 64 | 65 |
66 |
67 |
68 | 69 | 70 |

In addition to connect() and chain(), Mooog provides a Track object which is a special node that includes a pan/gain stage and a send() method that routes audio to another track, pre- or post-fader. The track object takes a string id as its first argument followed by one or more objects to define the nodes of its internal chain.

71 |

In this example, we've set up 3 Track objects: an AudioBuffer to play a sound, and two effects tracks for delay (sent post-fader) and a reverb (sent pre-fader). Since the returns are also Track objects, they have their own gain and pan controls.

72 |

In the code below, the slider change events are omitted for brevity. Track sends are just Gain objects so changing send levels after init is simply: M.track("balafon").send("delay_send").param("gain", new_gain_value);

73 | 74 |
<%= partial 'partials/tracks/example-1.js', locals: { new_mooog: true }  %>
75 | 76 | 77 | 78 | 79 | 80 | 81 | 84 | 85 | 86 | 87 |
88 |
89 | -------------------------------------------------------------------------------- /source/3.envelopes.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

ADSR

5 | 6 |
7 |
8 |
9 | 10 | Oscillator#saw 11 | 12 |
13 | 14 | 15 |
16 | 17 |
18 |
19 |
20 |

Parameter control envelopes are a fundamental part of electronic sound production. Mooog's adsr() method allows 21 | you to easily apply the traditional A(ttack) D(elay) S(ustain) R(elease) envelope to any AudioParam. You 22 | specify a base value, a peak attack value, a sustain value, and an array of durations for the phases of the envelope.

23 |
    24 |
  • 4 values produce a complete ADSR envelope
  • 25 |
  • 3 values produce a simpler ASR envelope
  • 26 |
  • If you're triggering sounds based on user input then you can give a two-value times array and 27 | only the attack, delay, and sustain portions of the envelope will be produced. You'll then need to use one of 28 | the other ramping functions to terminate the envelope when appropriate – remembering to set from_now: 29 | true or the ramps will be calculated beginning at the previously scheduled event instead of the time 30 | you callparam(). 31 |
  • 32 |
33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 |
<%= partial 'partials/envelopes/example-1.js', locals: { new_mooog: true }  %>
42 | 43 | 44 | 45 | 46 | 47 | 48 | 51 | 52 | 53 | 54 |
55 |
56 | -------------------------------------------------------------------------------- /source/9.browser-support.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

Browser support

5 | 6 |
7 |
8 |

The Web Audio API is not, and never will be, supported on IE (though it is supported on Edge), which 9 | limits its usefulness for general web projects until Edge supplants IE. Even where it is supported, the 10 | API still has not matured (AudioWorkers, for example, are not implemented anywhere yet) so Mooog doesn't 11 | worry too much about cross-browser compatibility issues. It does, however, implement a patch 12 | for the absence of the StereoPannerNode on for the deprecated Audio API (for Safari), since panning is such a 13 | basic audio operation and the Mooog `Track` object relies on it. Ensuring cross-platform 14 | consistency is on the to-do list once the API stabilizes and browser support improves.

15 | 16 |

Mooog.browser_test() returns an object with a set of boolean properties indicating whether your 17 | browser is current in its support of the spec.

18 | 19 | 20 |
    21 |
  • Your browser support the unprefixed AudioContext object.
  • 22 |
  • Your browser support the start and 23 | stop methods instead of noteon and noteoff.
  • 24 |
  • Your browser support the StereoPannerNode
  • 25 |
  • Your browser support the ScriptProcessorNode
  • 26 | 27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 50 | 51 | 52 | 53 |
54 |
55 | -------------------------------------------------------------------------------- /source/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mooog examples", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/mattlima/mooog", 5 | "authors": [ 6 | "Matt Lima " 7 | ], 8 | "license": "MIT", 9 | "private": true, 10 | "dependencies": { 11 | "foundation": "~5.5.2", 12 | "mooog": "https://github.com/mattlima/mooog.git", 13 | "Chart.js": "~1.0.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /source/fonts/aller-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/fonts/aller-bold.eot -------------------------------------------------------------------------------- /source/fonts/aller-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/fonts/aller-bold.ttf -------------------------------------------------------------------------------- /source/fonts/aller-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/fonts/aller-bold.woff -------------------------------------------------------------------------------- /source/fonts/aller-light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/fonts/aller-light.eot -------------------------------------------------------------------------------- /source/fonts/aller-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/fonts/aller-light.ttf -------------------------------------------------------------------------------- /source/fonts/aller-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/fonts/aller-light.woff -------------------------------------------------------------------------------- /source/fonts/novecento-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/fonts/novecento-bold.eot -------------------------------------------------------------------------------- /source/fonts/novecento-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/fonts/novecento-bold.ttf -------------------------------------------------------------------------------- /source/fonts/novecento-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/fonts/novecento-bold.woff -------------------------------------------------------------------------------- /source/index.html.erb: -------------------------------------------------------------------------------- 1 | --- 2 | title: Table of Contents 3 | layout: toc 4 | --- 5 |
6 |
7 | 8 |

9 | Comprehensive documentation can be found in the <%= link_to "README", "https://github.com/mattlima/mooog" %>. 10 |

11 | 12 |
    13 | <% data.examples.each do |example| %> 14 | 15 |
  1. <%= link_to example.name, toc_link(example.name) %>
  2. 16 | 17 | <% end %> 18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /source/javascripts/all.js: -------------------------------------------------------------------------------- 1 | // require_tree . 2 | //= require jquery/dist/jquery 3 | //= require foundation/js/foundation/foundation 4 | //= require foundation/js/foundation/foundation.equalizer 5 | //= require foundation/js/foundation/foundation.offcanvas 6 | //= require foundation/js/foundation/foundation.alert 7 | //= require foundation/js/foundation/foundation.tooltip 8 | //= require foundation/js/foundation/foundation.slider 9 | //= require foundation/js/foundation/foundation.reveal 10 | //= require foundation/js/foundation/foundation.topbar 11 | //= require mooog/dist/mooog 12 | -------------------------------------------------------------------------------- /source/javascripts/body-end.coffee: -------------------------------------------------------------------------------- 1 | $(document).foundation() 2 | 3 | if(!Mooog.browser_test().all) 4 | $('#not-supported').foundation('reveal', 'open') 5 | 6 | 7 | $('[data-slider]').on 'change.fndtn.slider', (e)-> 8 | $t = $(e.target) 9 | d = $t.data() 10 | val = $t.attr('data-slider') 11 | switch d.mooogTargetType 12 | when "send" 13 | M.track(d.mooogNodeTarget).send(d.mooogParamTarget).param("gain", val) 14 | else 15 | M.node(d.mooogNodeTarget).param(d.mooogParamTarget, val) 16 | -------------------------------------------------------------------------------- /source/javascripts/highlight/README.md: -------------------------------------------------------------------------------- 1 | # Highlight.js 2 | 3 | [![Build Status](https://travis-ci.org/isagalaev/highlight.js.svg?branch=master)](https://travis-ci.org/isagalaev/highlight.js) 4 | 5 | Highlight.js is a syntax highlighter written in JavaScript. It works in 6 | the browser as well as on the server. It works with pretty much any 7 | markup, doesn’t depend on any framework and has automatic language 8 | detection. 9 | 10 | ## Getting Started 11 | 12 | The bare minimum for using highlight.js on a web page is linking to the 13 | library along with one of the styles and calling 14 | [`initHighlightingOnLoad`][1]: 15 | 16 | ```html 17 | 18 | 19 | 20 | ``` 21 | 22 | This will find and highlight code inside of `
` tags; it tries
 23 | to detect the language automatically. If automatic detection doesn’t
 24 | work for you, you can specify the language in the `class` attribute:
 25 | 
 26 | ```html
 27 | 
...
28 | ``` 29 | 30 | The list of supported language classes is available in the [class 31 | reference][2]. Classes can also be prefixed with either `language-` or 32 | `lang-`. 33 | 34 | To disable highlighting altogether use the `nohighlight` class: 35 | 36 | ```html 37 |
...
38 | ``` 39 | 40 | ## Custom Initialization 41 | 42 | When you need a bit more control over the initialization of 43 | highlight.js, you can use the [`highlightBlock`][3] and [`configure`][4] 44 | functions. This allows you to control *what* to highlight and *when*. 45 | 46 | Here’s an equivalent way to calling [`initHighlightingOnLoad`][1] using 47 | jQuery: 48 | 49 | ```javascript 50 | $(document).ready(function() { 51 | $('pre code').each(function(i, block) { 52 | hljs.highlightBlock(block); 53 | }); 54 | }); 55 | ``` 56 | 57 | You can use any tags instead of `
` to mark up your code. If
 58 | you don't use a container that preserve line breaks you will need to
 59 | configure highlight.js to use the `
` tag: 60 | 61 | ```javascript 62 | hljs.configure({useBR: true}); 63 | 64 | $('div.code').each(function(i, block) { 65 | hljs.highlightBlock(block); 66 | }); 67 | ``` 68 | 69 | For other options refer to the documentation for [`configure`][4]. 70 | 71 | ## Getting the Library 72 | 73 | You can get highlight.js as a hosted, or custom-build, browser script or 74 | as a server module. Right out of the box the browser script supports 75 | both AMD and CommonJS, so if you wish you can use RequireJS or 76 | Browserify without having to build from source. The server module also 77 | works perfectly fine with Browserify, but there is the option to use a 78 | build specific to browsers rather than something meant for a server. 79 | Head over to the [download page][5] for all the options. 80 | 81 | **Note:** the library is not supposed to work straight from the source 82 | on GitHub; it requires building. If none of the pre-packaged options 83 | work for you refer to the [building documentation][6]. 84 | 85 | ## License 86 | 87 | Highlight.js is released under the BSD License. See [LICENSE][7] file 88 | for details. 89 | 90 | ## Links 91 | 92 | The official site for the library is at . 93 | 94 | Further in-depth documentation for the API and other topics is at 95 | . 96 | 97 | Authors and contributors are listed in the [AUTHORS.en.txt][8] file. 98 | 99 | [1]: http://highlightjs.readthedocs.org/en/latest/api.html#inithighlightingonload 100 | [2]: http://highlightjs.readthedocs.org/en/latest/css-classes-reference.html 101 | [3]: http://highlightjs.readthedocs.org/en/latest/api.html#highlightblock-block 102 | [4]: http://highlightjs.readthedocs.org/en/latest/api.html#configure-options 103 | [5]: https://highlightjs.org/download/ 104 | [6]: http://highlightjs.readthedocs.org/en/latest/building-testing.html 105 | [7]: https://github.com/isagalaev/highlight.js/blob/master/LICENSE 106 | [8]: https://github.com/isagalaev/highlight.js/blob/master/AUTHORS.en.txt 107 | -------------------------------------------------------------------------------- /source/javascripts/highlight/highlight.pack.js: -------------------------------------------------------------------------------- 1 | !function(e){"undefined"!=typeof exports?e(exports):(window.hljs=e({}),"function"==typeof define&&define.amd&&define("hljs",[],function(){return window.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&").replace(//gm,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){return/^(no-?highlight|plain|text)$/i.test(e)}function i(e){var n,t,r,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=/\blang(?:uage)?-([\w-]+)\b/i.exec(i))return w(t[1])?t[1]:"no-highlight";for(i=i.split(/\s+/),n=0,r=i.length;r>n;n++)if(w(i[n])||a(i[n]))return i[n]}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3==i.nodeType?a+=i.nodeValue.length:1==i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset"}function u(e){f+=""}function c(e){("start"==e.event?o:u)(e.node)}for(var s=0,f="",l=[];e.length||r.length;){var g=i();if(f+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){l.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g==e&&g.length&&g[0].offset==s);l.reverse().forEach(o)}else"start"==g[0].event?l.push(g[0].node):l.pop(),c(g.splice(0,1)[0])}return f+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):Object.keys(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\b\w+\b/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var f=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=f.length?t(f.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){for(var t=0;t";return i+=e+'">',i+n+o}function p(){if(!L.k)return n(y);var e="",t=0;L.lR.lastIndex=0;for(var r=L.lR.exec(y);r;){e+=n(y.substr(t,r.index-t));var a=g(L,r);a?(B+=a[1],e+=h(a[0],n(r[0]))):e+=n(r[0]),t=L.lR.lastIndex,r=L.lR.exec(y)}return e+n(y.substr(t))}function d(){var e="string"==typeof L.sL;if(e&&!x[L.sL])return n(y);var t=e?f(L.sL,y,!0,M[L.sL]):l(y,L.sL.length?L.sL:void 0);return L.r>0&&(B+=t.r),e&&(M[L.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){return void 0!==L.sL?d():p()}function v(e,t){var r=e.cN?h(e.cN,"",!0):"";e.rB?(k+=r,y=""):e.eB?(k+=n(t)+r,y=""):(k+=r,y=t),L=Object.create(e,{parent:{value:L}})}function m(e,t){if(y+=e,void 0===t)return k+=b(),0;var r=o(t,L);if(r)return k+=b(),v(r,t),r.rB?0:t.length;var a=u(L,t);if(a){var i=L;i.rE||i.eE||(y+=t),k+=b();do L.cN&&(k+=""),B+=L.r,L=L.parent;while(L!=a.parent);return i.eE&&(k+=n(t)),y="",a.starts&&v(a.starts,""),i.rE?0:t.length}if(c(t,L))throw new Error('Illegal lexeme "'+t+'" for mode "'+(L.cN||"")+'"');return y+=t,t.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,L=i||N,M={},k="";for(R=L;R!=N;R=R.parent)R.cN&&(k=h(R.cN,"",!0)+k);var y="",B=0;try{for(var C,j,I=0;;){if(L.t.lastIndex=I,C=L.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}for(m(t.substr(I)),R=L;R.parent;R=R.parent)R.cN&&(k+="");return{r:B,value:k,language:e,top:L}}catch(O){if(-1!=O.message.indexOf("Illegal"))return{r:0,value:n(t)};throw O}}function l(e,t){t=t||E.languages||Object.keys(x);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(w(n)){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function g(e){return E.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,E.tabReplace)})),E.useBR&&(e=e.replace(/\n/g,"
")),e}function h(e,n,t){var r=n?R[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n=i(e);if(!a(n)){var t;E.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):t=e;var r=t.textContent,o=n?f(n,r,!0):l(r),s=u(t);if(s.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=o.value,o.value=c(s,u(p),r)}o.value=g(o.value),e.innerHTML=o.value,e.className=h(e.className,n,o.language),e.result={language:o.language,re:o.r},o.second_best&&(e.second_best={language:o.second_best.language,re:o.second_best.r})}}function d(e){E=o(E,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){R[e]=n})}function N(){return Object.keys(x)}function w(e){return e=e.toLowerCase(),x[e]||x[R[e]]}var E={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},x={},R={};return e.highlight=f,e.highlightAuto=l,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},c:[{cN:"pi",r:10,b:/^\s*['"]use (strict|asm)['"]/},e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE,{cN:"subst",b:"\\$\\{",e:"\\}"}]},e.CLCM,e.CBCM,{cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/\s*[);\]]/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:[e.CLCM,e.CBCM]}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},{bK:"import",e:"[;$]",k:"import from as",c:[e.ASM,e.QSM]},{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]}],i:/#/}}); -------------------------------------------------------------------------------- /source/layouts/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%= current_page.data.title %> 11 | 12 | <%= stylesheet_link_tag "all" %> 13 | <%= javascript_include_tag "modernizr" %> 14 | <%= javascript_include_tag "all" %> 15 | <%= javascript_include_tag "highlight/highlight.pack" %> 16 | 17 | <% concat(partial "partials/ga") if build? %> 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | Mooog examples 26 |

<%= locals[:ordinal] %>. <%= locals[:name] %>

27 |
28 |
29 | 30 | 31 | <%= yield %> 32 | 33 | 34 |
35 |
36 | <% if(locals[:ordinal].to_i > 1) %> 37 | <% link_to toc_link(locals[:prev_example][:name]) do %> 38 |
<%= "<< #{locals[:ordinal] - 1}. #{locals[:prev_example][:name]}" %>
39 | <% end %> 40 | <% end %> 41 |
42 |
43 | <% if locals[:next_example].present? %> 44 | <% link_to toc_link(locals[:next_example][:name]) do %> 45 |
<%= "#{locals[:ordinal] + 1}. #{locals[:next_example][:name]} >>" %>
46 | <% end %> 47 | <% end %> 48 |
49 |
50 | <%= partial 'partials/footer', locals: locals %> 51 | 52 | 53 | <%= javascript_include_tag "body-end" %> 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /source/layouts/toc.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Mooog Examples | Table of Contents 11 | 12 | <%= stylesheet_link_tag "all" %> 13 | <%= javascript_include_tag "all" %> 14 | <%= javascript_include_tag "highlight/highlight.pack" %> 15 | 16 | <% concat(partial "partials/ga") if build? %> 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |

Mooog

25 | 26 |

Examples: Table of Contents

27 |
28 |
29 | 30 | <%= yield %> 31 | 32 | 33 | 34 | <%= javascript_include_tag "body-end" %> 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /source/partials/_footer.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

The fine print

5 |

Responsive design framework by <%= link_to("Zurb","http://foundation.zurb.com/") %>. 6 |

7 |

Automatic syntax highlighting by <%= link_to("highlight.js", "https://highlightjs.org/") %>. 8 |

9 |

The convolver examples use impulse responses from the <%= link_to('OpenAir Impulse Response Library', 'http://www.openairlib.net/') %> licensed under various Creative Commons licenses. 10 |

11 |
    12 |
  • <%= link_to("st-andrews-church-ortf-shaped.wav", "http://www.openairlib.net/auralizationdb/content/st-andrews-church") %> : <%= link_to("Attribution Share Alike Creative Commons license", "http://creativecommons.org/licenses/by-sa/3.0/") %>
  • 13 |
14 |

balafon.mp3 is the author's creation, released under the <%= link_to "MIT License", "https://opensource.org/licenses/MIT" %>

15 | 16 | 17 |
18 |
19 |
20 | 21 | <% if locals[:check_support] %> 22 |
23 |

AudioContext not fully supported

24 |

Your browser doesn't fully support the current AudioContext spec, so these examples may not function.

25 |

For more information, see the section on <%= link_to "browser support", toc_link("browser-support") %>.

26 |
27 | <% end %> 28 | -------------------------------------------------------------------------------- /source/partials/_ga.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/partials/_range_slider.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 |
7 | 8 | -------------------------------------------------------------------------------- /source/partials/basic-connections/_example-1.js.html.erb: -------------------------------------------------------------------------------- 1 | <% if locals[:new_mooog] %> 2 | M = new Mooog(); 3 | <% end %> 4 | M.node( { id: "osc", node_type: "Oscillator", type: "triangle" } ) 5 | .connect( 6 | M.node( { id: "short_delay", node_type: "Delay", delayTime: 0.25 } ) 7 | ) 8 | .connect( 9 | M.node( { id: "long_delay", node_type: "Delay", delayTime: 0.75 } ) 10 | ) 11 | 12 | $(document) 13 | .on("mousedown", ".trigger1", function(){ 14 | M.node("osc").start(); 15 | }) 16 | .on("mouseup", ".trigger1", function(){ 17 | M.node("osc").stop(); 18 | }) 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /source/partials/basic-connections/_example-2.js.html.erb: -------------------------------------------------------------------------------- 1 | <% if locals[:new_mooog] %> 2 | M = new Mooog(); 3 | <% end %> 4 | M.node( { id: "osc2", node_type: "Oscillator", type: "sawtooth" } ) 5 | .chain( 6 | M.node( { id: "filter", node_type: "BiquadFilter", frequency: 400 } ) 7 | ) 8 | .chain( 9 | M.node( { id: "pre-reverb", node_type: "Gain", gain: 0.6 } ) 10 | ) 11 | .chain( 12 | M.node( { id: "reverb", node_type: "Convolver", buffer_source_file: "sound/impulse-responses/st-andrews-church-ortf-shaped.mp3" } ) 13 | ) 14 | 15 | $(document) 16 | .on("mousedown", ".trigger2", function(){ 17 | M.node("osc2").start(); 18 | }) 19 | .on("mouseup", ".trigger2", function(){ 20 | M.node("osc2").stop(); 21 | }) 22 | -------------------------------------------------------------------------------- /source/partials/envelopes/_example-1.js.html.erb: -------------------------------------------------------------------------------- 1 | <% if locals[:new_mooog] %> 2 | M = new Mooog(); 3 | <% end %> 4 | M.track( "osc", 5 | { id: "saw", node_type: "Oscillator", type: "sawtooth" }, 6 | { id: "filter", node_type: "BiquadFilter", frequency: 300, Q: 30 }, 7 | { id: "amplitude", node_type: "Gain", gain: 0 } 8 | ); 9 | M.node('saw').start(); 10 | 11 | $(document) 12 | .on("mousedown", ".adsr1", function(){ 13 | M.node("filter").adsr( "frequency", { base: 300, a: 10000, s:2500, times: [0.5, 0.5, 0, 3] } ); 14 | M.node("amplitude").adsr( "gain", { base: 0, a: 1, s: 0.7, times: [0.1, 0.9, 0, 3] } ); 15 | }) 16 | .on("mousedown", ".adsr2", function(){ 17 | M.node("filter").adsr( "frequency", { base: 300, a: 10000, s:2500, times: [0.5, 0.5] } ); 18 | M.node("amplitude").adsr( "gain", { base: 0, a: 1, s: 0.7, times: [0.1, 0.9] } ); 19 | }) 20 | .on("mouseup", ".adsr2", function(){ 21 | M.node("filter").param( { frequency: 300, at: 3, ramp: "expo", from_now: true } ); 22 | M.node("amplitude").param( { gain: 0, at: 3, ramp: "expo", from_now: true } ); 23 | }) 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /source/partials/parameters/_example-1.js.html.erb: -------------------------------------------------------------------------------- 1 | <% if locals[:new_mooog] %> 2 | M = new Mooog(); 3 | <% end %> 4 | M.node( "osc", "Oscillator" ) 5 | .chain( 6 | M.node( { node_type: "Gain", gain: 0.5 } ) //don't blow any speakers 7 | ); 8 | 9 | $(document) 10 | .on("mousedown", ".trigger1", function(){ 11 | M.node("osc").start(); 12 | }) 13 | .on("mousedown", ".trigger2", function(){ 14 | M.node("osc").stop(); 15 | }) 16 | .on("click", "[data-waveform]", function(e){ 17 | M.node("osc").param( "type", $(this).data("waveform") ); 18 | }) 19 | .on("change.fndtn.slider", "#freq-slider", function(e){ 20 | val = $("#freq-slider").attr('data-slider') 21 | M.node( "osc" ).param( "frequency", val ) 22 | }) 23 | .on("change.fndtn.slider", "#detune-slider", function(e){ 24 | val = $("#detune-slider").attr('data-slider') 25 | M.node( "osc" ).param( "detune", val ) 26 | }) 27 | .on("click", ".reset1", function(){ 28 | M.node("osc").param( { type: 'sine', frequency: 440, detune: 0 } ); 29 | $("#detune-slider").foundation('slider', 'set_value', 0); 30 | $("#freq-slider").foundation('slider', 'set_value', 440); 31 | }) 32 | 33 | 34 | -------------------------------------------------------------------------------- /source/partials/parameters/_example-2.js.html.erb: -------------------------------------------------------------------------------- 1 | <% if locals[:new_mooog] %> 2 | M = new Mooog(); 3 | <% end %> 4 | M.node({id:"osc2", node_type:"Oscillator" }) 5 | .chain( 6 | M.node( { node_type: "Gain", gain: 0.5 } ) 7 | ); 8 | 9 | 10 | $(document) 11 | .on("mousedown",".trigger3",function(){ 12 | M.node("osc2").start(); 13 | }) 14 | .on("mousedown",".trigger4",function(){ 15 | M.node("osc2").stop(); 16 | }) 17 | .on("mousedown",".freq",function(){ 18 | M.node("osc2").param({frequency: 200}); 19 | }) 20 | .on("mousedown",".param1",function(){ 21 | M.node("osc2").param({frequency: 800, at: 1 }); 22 | }) 23 | .on("mousedown",".param2",function(){ 24 | M.node("osc2").param({frequency: 800, ramp: "linear", at: 2}); 25 | }) 26 | .on("mousedown",".param3",function(){ 27 | M.node("osc2").param({frequency: 800, ramp: "expo", at: 2}); 28 | }) 29 | .on("mousedown",".param4",function(){ 30 | M.node("osc2").param({frequency: [300, 350, 250, 450, 200, 800], ramp: "curve", at: 2, duration: 2}); 31 | }) 32 | .on("mousedown",".param5",function(){ 33 | M.node("osc2").param({frequency: 800, ramp: "expo", at: 2, timeConstant: 2}); 34 | }); 35 | -------------------------------------------------------------------------------- /source/partials/tracks/_example-1.js.html.erb: -------------------------------------------------------------------------------- 1 | <% if locals[:new_mooog] %> 2 | M = new Mooog(); 3 | <% end %> 4 | M.track( "balafon", 5 | { id: "ding", node_type: "AudioBufferSource", buffer_source_file: "sound/balafon1.mp3", loop: true }, 6 | { id: "compressor", node_type: "DynamicsCompressor", threshold: -30, ratio: 50 } //gross compression to demonstrate automatic chaining 7 | ); 8 | M.track( "delay", 9 | { node_type: "Delay", delayTime: 0.76, feedback: 0.2 } 10 | ); 11 | M.track( "reverb", 12 | { node_type: "Convolver", buffer_source_file: "sound/impulse-responses/st-andrews-church-ortf-shaped.mp3" } 13 | ); 14 | 15 | // track.send( id, destination, pre/post, initial_gain ) 16 | M.track("balafon").send( 'delay_send', M.track('delay'), 'post', 0.5); 17 | M.track("balafon").send( 'reverb_send', M.track('reverb'), 'pre', 0.25); 18 | 19 | $(document) 20 | .on("mousedown", ".ding.start", function(){ 21 | M.node("ding").start(); 22 | }) 23 | .on("mousedown", ".ding.stop", function(){ 24 | M.node("ding").stop(); 25 | }) 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /source/sound/balafon1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/sound/balafon1.mp3 -------------------------------------------------------------------------------- /source/sound/impulse-responses/radiostatic-shaped.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/sound/impulse-responses/radiostatic-shaped.wav -------------------------------------------------------------------------------- /source/sound/impulse-responses/sportscentre-york-ortf-shaped.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/sound/impulse-responses/sportscentre-york-ortf-shaped.wav -------------------------------------------------------------------------------- /source/sound/impulse-responses/st-andrews-church-ortf-shaped.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/sound/impulse-responses/st-andrews-church-ortf-shaped.mp3 -------------------------------------------------------------------------------- /source/sound/impulse-responses/st-andrews-church-ortf-shaped.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/sound/impulse-responses/st-andrews-church-ortf-shaped.wav -------------------------------------------------------------------------------- /source/sound/impulse-responses/st-marys-abbey-ortf-shaped.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/sound/impulse-responses/st-marys-abbey-ortf-shaped.wav -------------------------------------------------------------------------------- /source/sound/impulse-responses/terrys-typing-ortf-shaped.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/sound/impulse-responses/terrys-typing-ortf-shaped.wav -------------------------------------------------------------------------------- /source/sound/impulse-responses/univ-york-stairway-shaped.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlima/mooog/a5fa0f4a72e489e7f191ac831f4377a1c9ea859e/source/sound/impulse-responses/univ-york-stairway-shaped.wav -------------------------------------------------------------------------------- /source/stylesheets/_buttons.scss: -------------------------------------------------------------------------------- 1 | .button, button{ 2 | @include aller(); 3 | 4 | border: 1px solid $primary-color-dark; 5 | background-color: $primary-color-light; 6 | color: $primary-color-dark; 7 | padding: 0.6rem 0.8rem; 8 | transition: background-color, color 150ms ease-out; 9 | @include hover-dark(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /source/stylesheets/_footer.scss: -------------------------------------------------------------------------------- 1 | footer{ 2 | margin-top: 20px; 3 | 4 | h3{ 5 | font-size:1.5rem; 6 | } 7 | 8 | p, ul{ 9 | margin: 2px 0; 10 | font-size:0.8rem; 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /source/stylesheets/_global.scss: -------------------------------------------------------------------------------- 1 | .row{ 2 | max-width: 80rem; 3 | } 4 | 5 | a{ 6 | color:$primary-color-dark; 7 | @include aller-bold(); 8 | &:hover{ 9 | color:$primary-color-medium; 10 | } 11 | } 12 | 13 | .noMarginTop{ 14 | margin-top:0; 15 | } 16 | 17 | ol.toc{ 18 | font-size:20px; 19 | margin-left:2rem; 20 | } 21 | 22 | span.supertitle{ 23 | margin-top:10px; 24 | } 25 | 26 | .panel.sound{ 27 | background-color: #F6F6F6; 28 | } 29 | 30 | .label{ 31 | background-color: $primary-color-dark; 32 | color: white; 33 | @include novecento(); 34 | } 35 | 36 | fieldset.sound{ 37 | legend{ 38 | @include aller-bold(); 39 | @include radius(); 40 | background-color: $primary-color-dark; 41 | color: white; 42 | padding:0.2rem 0.7rem; 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /source/stylesheets/_hljs.scss: -------------------------------------------------------------------------------- 1 | /* Atelier-Seaside Comment */ 2 | .hljs-comment { 3 | color: #687d68; 4 | } 5 | 6 | /* Atelier-Seaside Red */ 7 | .hljs-variable, 8 | .hljs-attribute, 9 | .hljs-tag, 10 | .hljs-regexp, 11 | .hljs-name, 12 | .ruby .hljs-constant, 13 | .xml .hljs-tag .hljs-title, 14 | .xml .hljs-pi, 15 | .xml .hljs-doctype, 16 | .html .hljs-doctype, 17 | .css .hljs-id, 18 | .css .hljs-class, 19 | .css .hljs-pseudo { 20 | color: $hljs-variable; 21 | } 22 | 23 | /* Atelier-Seaside Orange */ 24 | .hljs-number, 25 | .hljs-preprocessor, 26 | .hljs-built_in, 27 | .hljs-literal, 28 | .hljs-params, 29 | .hljs-constant { 30 | color: $hljs-number; 31 | } 32 | 33 | /* Atelier-Seaside Yellow */ 34 | .ruby .hljs-class .hljs-title, 35 | .css .hljs-rule .hljs-attribute { 36 | color: $hljs-class; 37 | } 38 | 39 | /* Atelier-Seaside Green */ 40 | .hljs-string, 41 | .hljs-value, 42 | .hljs-inheritance, 43 | .hljs-header, 44 | .ruby .hljs-symbol, 45 | .xml .hljs-cdata { 46 | color: $hljs-string; 47 | } 48 | 49 | /* Atelier-Seaside Aqua */ 50 | .hljs-title, 51 | .css .hljs-hexcolor { 52 | color: $hljs-title; 53 | } 54 | 55 | /* Atelier-Seaside Blue */ 56 | .hljs-function, 57 | .python .hljs-decorator, 58 | .python .hljs-title, 59 | .ruby .hljs-function .hljs-title, 60 | .ruby .hljs-title .hljs-keyword, 61 | .perl .hljs-sub, 62 | .javascript .hljs-title, 63 | .coffeescript .hljs-title { 64 | color: $hljs-function; 65 | } 66 | 67 | /* Atelier-Seaside Purple */ 68 | .hljs-keyword, 69 | .javascript .hljs-function { 70 | color: $hljs-keyword; 71 | } 72 | 73 | .hljs { 74 | display: block; 75 | overflow-x: auto; 76 | background: $hljs-background; 77 | color: $hljs-color; 78 | padding: 1em; 79 | -webkit-text-size-adjust: none; 80 | } 81 | 82 | .coffeescript .javascript, 83 | .javascript .xml, 84 | .tex .hljs-formula, 85 | .xml .javascript, 86 | .xml .vbscript, 87 | .xml .css, 88 | .xml .hljs-cdata { 89 | opacity: 0.5; 90 | } 91 | 92 | pre code{ 93 | margin: 20px 0; 94 | } 95 | -------------------------------------------------------------------------------- /source/stylesheets/_range-slider.scss: -------------------------------------------------------------------------------- 1 | .range-slider.round{ 2 | margin-bottom:2rem; 3 | 4 | .range-slider-handle{ 5 | background-color: $primary-color-medium; 6 | border:1px solid $primary-color-dark; 7 | @include hover-dark(); 8 | } 9 | label{ 10 | position: absolute; 11 | top: -21px; 12 | left: 0; 13 | font-size: 13px; 14 | @include aller-bold(); 15 | color: $primary-color-dark; 16 | span{ 17 | display:inline-block; 18 | margin-left:10px; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /source/stylesheets/_typography.scss: -------------------------------------------------------------------------------- 1 | 2 | /*--------------------- Typography ----------------------------*/ 3 | 4 | @font-face { 5 | font-family: 'aller-light'; 6 | src: url('../fonts/aller-light.eot'); 7 | src: url('../fonts/aller-light.eot?#iefix') format('embedded-opentype'), 8 | url('../fonts/aller-light.woff') format('woff'), 9 | url('../fonts/aller-light.ttf') format('truetype'); 10 | font-weight: normal; 11 | font-style: normal; 12 | } 13 | 14 | @font-face { 15 | font-family: 'aller-bold'; 16 | src: url('../fonts/aller-bold.eot'); 17 | src: url('../fonts/aller-bold.eot?#iefix') format('embedded-opentype'), 18 | url('../fonts/aller-bold.woff') format('woff'), 19 | url('../fonts/aller-bold.ttf') format('truetype'); 20 | font-weight: normal; 21 | font-style: normal; 22 | } 23 | 24 | @font-face { 25 | font-family: 'novecento-bold'; 26 | src: url('../fonts/novecento-bold.eot'); 27 | src: url('../fonts/novecento-bold.eot?#iefix') format('embedded-opentype'), 28 | url('../fonts/novecento-bold.woff') format('woff'), 29 | url('../fonts/novecento-bold.ttf') format('truetype'); 30 | font-weight: normal; 31 | font-style: normal; 32 | } 33 | 34 | @mixin novecento(){ 35 | font-family: 'novecento-bold', sans-serif; 36 | } 37 | 38 | @mixin aller(){ 39 | font-family: 'aller-light', sans-serif; 40 | } 41 | 42 | @mixin aller-bold(){ 43 | font-family: 'aller-bold', sans-serif; 44 | } 45 | 46 | 47 | html, body{ 48 | font-family: 'aller-light', sans-serif; 49 | font-size:95%; 50 | } 51 | 52 | strong{ 53 | font-family: 'aller-bold', sans-serif; 54 | } 55 | 56 | h1,h2{ 57 | @include novecento(); 58 | @include title-shadow(); 59 | &.mooog-title{ 60 | background-color: $primary-color-dark; 61 | display: inline-block; 62 | padding: 0.5rem 1rem; 63 | color: white; 64 | @include radius(); 65 | } 66 | } 67 | 68 | h3,h4,h5,h6{ 69 | @include aller-bold(); 70 | @include title-shadow(); 71 | } 72 | 73 | span.supported{ 74 | color: green; 75 | @include aller-bold(); 76 | } 77 | span.unsupported{ 78 | color: red; 79 | @include aller-bold(); 80 | } 81 | -------------------------------------------------------------------------------- /source/stylesheets/_variables.scss: -------------------------------------------------------------------------------- 1 | 2 | $primary-color-dark: #29a329; 3 | $primary-color-medium: #b0d0b0; 4 | $primary-color-light: #e4ebe4; 5 | 6 | 7 | $hljs-background: #f4fbf4; 8 | $hljs-color: #5e6e5e; 9 | $hljs-variable: #e6193c; 10 | $hljs-number: #87711d; 11 | $hljs-class:#98981b; 12 | $hljs-string: $primary-color-dark; 13 | $hljs-title: #1999b3; 14 | $hljs-function: #3d62f5; 15 | $hljs-keyword: #ad2bee; 16 | 17 | 18 | @mixin hover-dark(){ 19 | &:hover, &:focus{ 20 | background-color: $primary-color-dark; 21 | } 22 | } 23 | 24 | @mixin title-shadow(){ 25 | text-shadow: rgba(0,0,0,0.2) 2px 2px 2px; 26 | } 27 | -------------------------------------------------------------------------------- /source/stylesheets/all.css.scss: -------------------------------------------------------------------------------- 1 | @import "normalize.scss"; 2 | @import "foundation.scss"; 3 | @import "variables"; 4 | @import "typography"; 5 | @import "hljs"; 6 | @import "global"; 7 | @import "range-slider"; 8 | @import "buttons"; 9 | @import "footer"; 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Mooog.litcoffee: -------------------------------------------------------------------------------- 1 | ## Mooog 2 | 3 | Wraps the AudioContext object and exposes the Track object. Exposes the API 4 | as the Mooog global. 5 | 6 | 7 | class Mooog 8 | 9 | @LEGAL_NODES: 10 | 11 | 'Analyser': Analyser 12 | 'AudioBufferSource': AudioBufferSource 13 | 'BiquadFilter': BiquadFilter 14 | 'ChannelMerger': ChannelMerger 15 | 'ChannelSplitter': ChannelSplitter 16 | 'Convolver': Convolver 17 | 'Delay': Delay 18 | 'DynamicsCompressor': DynamicsCompressor 19 | 'Gain': Gain 20 | 'MediaElementSource': MediaElementSource 21 | 'Oscillator': Oscillator 22 | 'Panner': Panner 23 | 'ScriptProcessor': ScriptProcessor 24 | 'StereoPanner': StereoPanner 25 | 'WaveShaper': WaveShaper 26 | 27 | @MooogAudioNode = MooogAudioNode 28 | 29 | @EVENT_NAMES: 30 | AUDIO_BUFFER_LOADED: 'mooog.audioBufferLoaded' 31 | 32 | 33 | 34 | 35 | 36 | constructor: (@initConfig = {}) -> 37 | @config = 38 | debug: false 39 | default_gain: 0.5 40 | default_ramp_type: 'expo' 41 | default_send_type: 'post' 42 | periodic_wave_length: 2048 43 | curve_length: 65536 44 | fake_zero: 1/65536 45 | allow_multiple_audiocontexts: false 46 | 47 | 48 | It's not unusual to have several AudioBufferNodes using the same buffer source 49 | file. If they are initialized at the same time, each one will make an HTTP 50 | request for the file, resulting in a lot of unnecessary network traffic. We store 51 | the URLs of audio assets here to make sure they're only loaded once by any object that needs them 52 | 53 | 54 | @audioBuffersLoaded = {} 55 | 56 | 57 | `_BROWSER_CONSTRUCTOR` Stores the type of constructor used for the AudioContext 58 | object (`AudioContext` or `webkitAudioContext`) 59 | 60 | @_BROWSER_CONSTRUCTOR = false 61 | @context = @create_context() 62 | @_destination = @context.destination 63 | 64 | @init(@initConfig) 65 | 66 | Mooog.browser_test() 67 | @iOS_setup() 68 | @_nodes = {} 69 | #@_connections = {} 70 | @__typeof = MooogAudioNode.prototype.__typeof 71 | console.log "AudioContext not fully supported in this browser. 72 | Run Mooog.browser_test() for more info" unless Mooog.browser_test().all 73 | 74 | 75 | ### Mooog.iOS_setup 76 | A little hocus pocus to get around Apple's restrictions on sound production before 77 | use input. Taken from 78 | https://github.com/shinnn/AudioContext-Polyfill/blob/master/audiocontext-polyfill.js 79 | 80 | iOS_setup: () -> 81 | is_iOS = (navigator.userAgent.indexOf('like Mac OS X') isnt -1) 82 | if is_iOS 83 | body = document.body 84 | tmpBuf = @context.createBufferSource() 85 | tmpProc = @context.createScriptProcessor(256, 1, 1) 86 | instantProcess = () => 87 | tmpBuf.start(0) 88 | tmpBuf.connect(tmpProc) 89 | tmpProc.connect(@context.destination) 90 | body.addEventListener('touchstart', instantProcess, false) 91 | tmpProc.onaudioprocess = () -> 92 | tmpBuf.disconnect() 93 | tmpProc.disconnect() 94 | body.removeEventListener('touchstart', instantProcess, false) 95 | tmpProc.onaudioprocess = null 96 | 97 | 98 | init: (initConfig) -> 99 | for key, val of @config 100 | if initConfig[key]? 101 | @config[key] = initConfig[key] 102 | null 103 | 104 | 105 | @context = false 106 | 107 | create_context: () -> 108 | @_BROWSER_CONSTRUCTOR = switch 109 | when window.AudioContext? then 'AudioContext' 110 | when window.webkitAudioContext? then 'webkitAudioContext' 111 | else throw new Error("This browser does not yet support the AudioContext API") 112 | if @config.allow_multiple_audiocontexts 113 | return new window[@_BROWSER_CONSTRUCTOR] 114 | Mooog.context || Mooog.context = new window[@_BROWSER_CONSTRUCTOR] 115 | 116 | 117 | 118 | 119 | ### Mooog.track 120 | 121 | Creates a Track object holds a chain of AudioNodes. By default it includes panner 122 | and gain stages. 123 | 124 | `id`: A unique identifier to assign to this Track 125 | `node_list`: One or more config objects or `MooogAudioNode` objects to add to the `Track` 126 | 127 | 128 | track: (id, node_list...) -> 129 | return new Track(this) unless arguments.length 130 | if typeof id is 'string' 131 | if node_list.length 132 | throw new Error("#{id} is already assigned to #{@_nodes[id]}") if @_nodes[id]? 133 | @_nodes[id] = new Track(this, { id:id }) 134 | @_nodes[id].add node_list 135 | return @_nodes[id] 136 | else if @_nodes?[id]? 137 | return @_nodes[id] 138 | else throw new Error("No Track found with id #{id}") 139 | else 140 | throw new Error("Track id must be a string") 141 | 142 | 143 | ### Mooog.node 144 | 145 | Creates new nodes or chains of nodes. Signatures: 146 | 147 | > node(id:string, node_type:string) 148 | 149 | `id`: A unique identifier to assign to this Node 150 | `node_type`: String representing the type of Node to initialize 151 | 152 | 153 | > node(node_definition:object) 154 | > node(id:string, node_definition:object [, node_definition...]) 155 | 156 | `node_definition`: An object used to create and configure the new Node. 157 | 158 | Required properties: 159 | - `node_type`: String indicating the type of Node (Oscillator, Gain, etc.) 160 | 161 | Optional properties for all nodes 162 | - `id`: Unique string identifier, will be created programatically if not given. 163 | - `connect_to_destination`: Boolean indicating whether the last in this node's 164 | `_nodes` array is automatically connected to the `AudioDestinationNode`. *default: true* 165 | 166 | Additional properties 167 | - Any additional key-value pairs will be used to set properties of the underlying `AudioNode` 168 | object after initialization. 169 | 170 | If more than one `node_definition` object is given, the first argument must be a string id 171 | to assign to a `MooogAudioNode` base node. The nodes will be added to its internal chain 172 | according to the `node_definition` objects. 173 | 174 | 175 | node: () -> 176 | arg0 = arguments[0] 177 | arg1 = arguments[1] 178 | type0 = @__typeof arg0 179 | type1 = @__typeof arg1 180 | 181 | 182 | Take care of first type signature (ID and string type) 183 | 184 | 185 | if type0 is "string" and type1 is "string" 186 | if Mooog.LEGAL_NODES[arg1]? 187 | if @_nodes[arg0] 188 | throw new Error("#{arg0} is already assigned to #{@_nodes[arg0]}") 189 | @_nodes[arg0] = new Mooog.LEGAL_NODES[arg1] @, { id: arg0, node_type: arg1 } 190 | else 191 | console.log(arguments) 192 | throw new Error "Unknown node type #{arg1}" 193 | 194 | 195 | This might be a request for an existing node 196 | 197 | 198 | else if type0 is "string" and type1 is "undefined" 199 | if @_nodes[arg0] 200 | return @_nodes[arg0] 201 | else 202 | throw new Error("No MooogAudioNode found with id #{arg0}") 203 | 204 | 205 | Is it a single `node_definition` object? 206 | 207 | 208 | else if type0 is "object" and type1 is "undefined" 209 | if @_nodes[arg0.id] 210 | throw new Error("#{arg0.id} is already assigned to #{@_nodes[arg0.id]}") 211 | else if Mooog.LEGAL_NODES[arg0.node_type]? 212 | new_node = new Mooog.LEGAL_NODES[arg0.node_type] @, arg0 213 | @_nodes[new_node.id] = new_node 214 | else 215 | throw new Error("Omitted or undefined node type in config options.") 216 | 217 | Or an array of `node_definition` objects 218 | 219 | 220 | else if type0 is "object" and type1 is "object" 221 | throw new Error "A string id for the base node must be provided if you give 222 | more than one node definition" 223 | 224 | else if type0 is "string" and type1 is "object" 225 | new_node = new MooogAudioNode @, {id: arg0} 226 | @_nodes[new_node.id] = new_node 227 | for i in arguments 228 | new_node.add new MooogAudioNode @, i 229 | 230 | 231 | 232 | 233 | ### Mooog.extend_with 234 | 235 | Adds a new node definition from defined after initialization. At a minimum, the node definition 236 | looks like: 237 | 238 | ``` coffeescript 239 | class MyNewNode extends Mooog.MooogAudioNode 240 | constructor: (@_instance, config)-> 241 | super 242 | 243 | #this hook runs before the configuration object is parsed 244 | before_config: (config)-> 245 | #do something here like create the internal node chain 246 | 247 | #this hook runs after the configuration object is parsed 248 | after_config: (config)-> 249 | #add additional nodes, perform post-config settings 250 | 251 | #all object properties defined will be available on the node 252 | some_new_function: (e) -> 253 | 254 | 255 | #Then run this command to sanity-check the node and make it available via the Mooog instance 256 | Mooog.extend_with "MyNewNodeName", MyNewNode 257 | ``` 258 | 259 | 260 | @extend_with: (nodeName, nodeDef) -> 261 | window.nodeDef = nodeDef 262 | if !nodeDef.prototype.before_config? 263 | throw new Error "Node definition prototype must have a before_config function" 264 | if !nodeDef.prototype.after_config? 265 | throw new Error "Node definition prototype must have a before_config function" 266 | if Mooog.LEGAL_NODES[nodeName]? 267 | throw new Error "#{nodeName} class already defined" 268 | Mooog.LEGAL_NODES[nodeName] = nodeDef 269 | null 270 | 271 | 272 | 273 | 274 | 275 | 276 | ### Mooog.freq 277 | Convenience function for converting MIDI notes to equal temperament Hz 278 | 279 | 280 | @freq: (n) -> 281 | 440 * Math.pow(2,((n-69)/12)) 282 | 283 | 284 | sawtoothPeriodicWave: (harms) -> 285 | harms ?= @config.periodic_wave_length 286 | a = [0] 287 | a.push(1/i) for i in [1..harms-1] 288 | real = new Float32Array(a) 289 | imag = new Float32Array(real.length) 290 | return @context.createPeriodicWave(real, imag) 291 | 292 | 293 | squarePeriodicWave: (harms) -> 294 | harms ?= @config.periodic_wave_length 295 | a = [0] 296 | for i in [1..harms-1] 297 | if i%2 != 0 298 | a.push(2/(Math.PI * i)) 299 | else 300 | a.push(0) 301 | real = new Float32Array(a) 302 | imag = new Float32Array(real.length) 303 | return @context.createPeriodicWave(real, imag) 304 | 305 | 306 | trianglePeriodicWave: (harms) -> 307 | harms ?= @config.periodic_wave_length 308 | a = [0] 309 | for i in [1..harms-1] 310 | if i%2 != 0 311 | a.push(1/(Math.pow(i,2))) 312 | else 313 | a.push(0) 314 | real = new Float32Array(a) 315 | imag = new Float32Array(real.length) 316 | return @context.createPeriodicWave(real, imag) 317 | 318 | 319 | sinePeriodicWave: (harms) -> 320 | a = [0, 1] 321 | real = new Float32Array(a) 322 | imag = new Float32Array(real.length) 323 | return @context.createPeriodicWave(real, imag) 324 | 325 | 326 | 327 | ### Mooog.browser_test 328 | Tests parts of the API to see whether Mooog will run correctly in a browser. Attempts to 329 | patch a few of them (StereoPanner, noteOn/NoteOff), and returns an object of test results 330 | including an `all` property which should indicate whether Mooog can do its thing, or simply 331 | false if the AudioContext API is not supported at all. 332 | 333 | @brower_test_results: false 334 | 335 | @browser_test: ()-> 336 | if @browser_test_results 337 | return @browser_test_results 338 | tests = { all: true } 339 | ctxt = window.AudioContext || window.webkitAudioContext 340 | tests.all = if (tests.audio_context = !!ctxt) then tests.all else false 341 | return false if !ctxt 342 | __t = new ctxt() 343 | tests.all = if (tests.unprefixed = window.AudioContext?) then tests.all else false 344 | tests.all = if (tests.start_stop = __t.createOscillator().start?) then tests.all else false 345 | if __t.createStereoPanner? 346 | tests.stereo_panner = true 347 | else 348 | try 349 | @patch_StereoPanner() 350 | tests.stereo_panner = 'patched' 351 | catch error 352 | test.stereo_panner = false 353 | tests.all = false 354 | tests.all = if (tests.script_processor = __t.createScriptProcessor?) 355 | then tests.all else false 356 | @browser_test_results = tests 357 | 358 | 359 | 360 | ### Mooog.patch_StereoPanner 361 | Safari currently lacks support for the StereoPanner node. This function patches it. 362 | Adapted from 363 | (https://github.com/mohayonao/stereo-panner-node)[https://github.com/mohayonao/stereo-panner-node] 364 | 365 | 366 | 367 | 368 | @patch_StereoPanner: () -> 369 | WS_CURVE_SIZE = 4096 370 | curveL = new Float32Array(WS_CURVE_SIZE) 371 | curveR = new Float32Array(WS_CURVE_SIZE) 372 | 373 | for i in [0..WS_CURVE_SIZE] 374 | curveL[i] = Math.cos((i / WS_CURVE_SIZE) * Math.PI * 0.5) 375 | curveR[i] = Math.sin((i / WS_CURVE_SIZE) * Math.PI * 0.5) 376 | 377 | ### 378 | 379 | * StereoPannerImpl 380 | * +--------------------------------+ +------------------------+ 381 | * | ChannelSplitter(inlet) | | BufferSourceNode(_dc1) | 382 | * +--------------------------------+ | buffer: [ 1, 1 ] | 383 | * | | | loop: true | 384 | * | | +------------------------+ 385 | * | | | 386 | * | | +----------------+ 387 | * | | | GainNode(_pan) | 388 | * | | | gain: 0(pan) | 389 | * | | +----------------+ 390 | * | | | 391 | * | +-----------------------|----+ 392 | * | | | | 393 | * | +----------------------+ | +----------------------+ 394 | * | | WaveShaperNode(_wsL) | | | WaveShaperNode(_wsR) | 395 | * | | curve: curveL | | | curve: curveR | 396 | * | +----------------------+ | +----------------------+ 397 | * | | | | 398 | * | | | | 399 | * | | | | 400 | * +--------------+ | +--------------+ | 401 | * | GainNode(_L) | | | GainNode(_R) | | 402 | * | gain: 0 <----+ | gain: 0 <----+ 403 | * +--------------+ +--------------+ 404 | * | | 405 | * +--------------------------------+ 406 | * | ChannelMergerNode(outlet) | 407 | * +--------------------------------+ 408 | ### 409 | 410 | class StereoPannerImpl 411 | 412 | constructor: (audioContext) -> 413 | @audioContext = audioContext 414 | @inlet = audioContext.createChannelSplitter(2) 415 | @_pan = audioContext.createGain() 416 | @pan = @_pan.gain 417 | @_wsL = audioContext.createWaveShaper() 418 | @_wsR = audioContext.createWaveShaper() 419 | @_L = audioContext.createGain() 420 | @_R = audioContext.createGain() 421 | @outlet = audioContext.createChannelMerger(2) 422 | 423 | @inlet.channelCount = 2 424 | @inlet.channelCountMode = "explicit" 425 | @_pan.gain.value = 0 426 | @_wsL.curve = curveL 427 | @_wsR.curve = curveR 428 | @_L.gain.value = 0 429 | @_R.gain.value = 0 430 | 431 | @inlet.connect(@_L, 0) 432 | @inlet.connect(@_R, 1) 433 | @_L.connect(@outlet, 0, 0) 434 | @_R.connect(@outlet, 0, 1) 435 | @_pan.connect(@_wsL) 436 | @_pan.connect(@_wsR) 437 | @_wsL.connect(@_L.gain) 438 | @_wsR.connect(@_R.gain) 439 | 440 | @_isConnected = false 441 | @_dc1buffer = null 442 | @_dc1 = null 443 | 444 | connect: (destination) -> 445 | audioContext = @audioContext 446 | 447 | if (!@_isConnected) 448 | @_isConnected = true 449 | @_dc1buffer = audioContext.createBuffer(1, 2, audioContext.sampleRate) 450 | @_dc1buffer.getChannelData(0).set([ 1, 1 ]) 451 | 452 | @_dc1 = audioContext.createBufferSource() 453 | @_dc1.buffer = @_dc1buffer 454 | @_dc1.loop = true 455 | @_dc1.start(audioContext.currentTime) 456 | @_dc1.connect(@_pan) 457 | 458 | AudioNode.prototype.connect.call(@outlet, destination) 459 | 460 | 461 | disconnect: () -> 462 | @audioContext 463 | 464 | if (@_isConnected) 465 | @_isConnected = false 466 | @_dc1.stop(audioContext.currentTime) 467 | @_dc1.disconnect() 468 | @_dc1 = null 469 | @_dc1buffer = null 470 | 471 | AudioNode.prototype.disconnect.call(@outlet) 472 | 473 | 474 | 475 | 476 | 477 | class StereoPanner 478 | 479 | constructor: (audioContext) -> 480 | impl = new StereoPannerImpl(audioContext) 481 | 482 | Object.defineProperties(impl.inlet, 483 | pan: 484 | value: impl.pan, 485 | enumerable: true 486 | connect: 487 | value: (node) -> 488 | return impl.connect(node) 489 | disconnect: 490 | value: () -> 491 | return impl.disconnect() 492 | ) 493 | 494 | return impl.inlet 495 | 496 | 497 | ctxt = window.AudioContext || window.webkitAudioContext 498 | if (!ctxt || ctxt.prototype.hasOwnProperty("createStereoPanner")) 499 | return 500 | else 501 | ctxt.prototype.createStereoPanner = () -> 502 | return new StereoPanner(this) 503 | 504 | 505 | window.Mooog = Mooog 506 | -------------------------------------------------------------------------------- /src/nodes/Analyser.litcoffee: -------------------------------------------------------------------------------- 1 | ## Analyser 2 | 3 | Wraps the AnalyserNode AudioContext object 4 | 5 | class Analyser extends MooogAudioNode 6 | constructor: (@_instance, config = {}) -> 7 | super 8 | 9 | before_config: (config)-> 10 | @insert_node @context.createAnalyser(), 0 11 | 12 | after_config: (config)-> 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/nodes/AudioBufferSource.litcoffee: -------------------------------------------------------------------------------- 1 | ## AudioBufferSource 2 | `start()` and `stop()` are patched to work repeatedly on the same node by regenerating 3 | the underlying `AudioBufferSourceNode` 4 | 5 | 6 | Wraps the AudioBufferSourceNode AudioContext object 7 | 8 | class AudioBufferSource extends MooogAudioNode 9 | constructor: (@_instance, config = {}) -> 10 | super 11 | 12 | before_config: (config)-> 13 | @insert_node @context.createBufferSource(), 0 14 | @define_buffer_source_properties() 15 | 16 | after_config: (config)-> 17 | @insert_node new Gain @_instance, { 18 | gain: 1.0 19 | connect_to_destination: @config.connect_to_destination 20 | } 21 | @_state = 'stopped' 22 | @define_readonly_property 'state', () => 23 | @_state 24 | 25 | 26 | 27 | start: () -> 28 | return @ if @_state is 'playing' 29 | @_state = 'playing' 30 | @_nodes[1].param('gain', 1) 31 | @_nodes[0].start() 32 | this 33 | 34 | stop: () -> 35 | return @ if @_state is 'stopped' 36 | @_state = 'stopped' 37 | @_nodes[1].param('gain',0) 38 | new_source = @context.createBufferSource() 39 | @clone_AudioNode_properties @_nodes[0], new_source 40 | @delete_node 0 41 | @insert_node new_source, 0 42 | @expose_properties_of @_nodes[0] 43 | this 44 | 45 | 46 | clone_AudioNode_properties: (source, dest) -> 47 | for k, v of source 48 | switch @__typeof source[k] 49 | when 'AudioBuffer', 'boolean', 'number', 'string' 50 | dest[k] = v 51 | when 'AudioParam' then dest[k].value = v.value 52 | when 'function' 53 | dest[k] = v unless source[k].toString().match(/native code/) 54 | null 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/nodes/BiquadFilter.litcoffee: -------------------------------------------------------------------------------- 1 | ## BiquadFilter 2 | 3 | Wraps the BiquadFilterNode AudioContext object 4 | 5 | class BiquadFilter extends MooogAudioNode 6 | constructor: (@_instance, config = {}) -> 7 | super 8 | 9 | before_config: (config)-> 10 | @insert_node @context.createBiquadFilter(), 0 11 | 12 | after_config: (config)-> 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/nodes/ChannelMerger.litcoffee: -------------------------------------------------------------------------------- 1 | ## ChannelMerger 2 | 3 | Wraps the ChannelMergerNode AudioContext object. The `numberOfInputs` argument 4 | of the native constructor can be passed in the configuration object. 5 | 6 | class ChannelMerger extends MooogAudioNode 7 | constructor: (@_instance, config = {}) -> 8 | @__numberOfInputs = if config.numberOfInputs? then config.numberOfInputs else 6 9 | delete config.numberOfInputs 10 | super 11 | 12 | before_config: (config)-> 13 | @insert_node @context.createChannelMerger( @__numberOfInputs ), 0 14 | 15 | 16 | after_config: (config)-> 17 | 18 | -------------------------------------------------------------------------------- /src/nodes/ChannelSplitter.litcoffee: -------------------------------------------------------------------------------- 1 | ## ChannelSplitter 2 | 3 | Wraps the ChannelSplitterNode AudioContext object. The `numberOfOutputs` argument 4 | of the native constructor can be passed in the configuration object. 5 | 6 | class ChannelSplitter extends MooogAudioNode 7 | constructor: (@_instance, config = {}) -> 8 | @__numberOfOutputs = if config.numberOfOutputs? then config.numberOfOutputs else 6 9 | delete config.numberOfOutputs 10 | super 11 | 12 | before_config: (config)-> 13 | @insert_node @context.createChannelSplitter( @__numberOfOutputs ), 0 14 | 15 | after_config: (config)-> 16 | -------------------------------------------------------------------------------- /src/nodes/Convolver.litcoffee: -------------------------------------------------------------------------------- 1 | ## Convolver 2 | 3 | Wraps the ConvolverNode AudioContext object 4 | 5 | class Convolver extends MooogAudioNode 6 | constructor: (@_instance, config = {}) -> 7 | super 8 | 9 | before_config: (config)-> 10 | @insert_node @context.createConvolver(), 0 11 | @define_buffer_source_properties() 12 | 13 | after_config: (config)-> 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/nodes/Delay.litcoffee: -------------------------------------------------------------------------------- 1 | ## Delay 2 | 3 | Wraps the DelayNode AudioContext object. Has a built-in `feedback` 4 | parameter (off by default). 5 | 6 | class Delay extends MooogAudioNode 7 | constructor: (@_instance, config = {}) -> 8 | super 9 | 10 | before_config: (config)-> 11 | @insert_node @context.createDelay(), 0 12 | @_feedback_stage = new Gain( @_instance, { connect_to_destination: false, gain: 0 } ) 13 | @_nodes[0].connect @to @_feedback_stage 14 | @_feedback_stage.connect @to @_nodes[0] 15 | @feedback = @_feedback_stage.gain 16 | 17 | after_config: (config)-> 18 | -------------------------------------------------------------------------------- /src/nodes/DynamicsCompressor.litcoffee: -------------------------------------------------------------------------------- 1 | ## DynamicsCompressor 2 | 3 | Wraps the DynamicsCompressorNode AudioContext object 4 | 5 | class DynamicsCompressor extends MooogAudioNode 6 | constructor: (@_instance, config = {}) -> 7 | super 8 | 9 | before_config: (config)-> 10 | @insert_node @context.createDynamicsCompressor(), 0 11 | 12 | after_config: (config)-> 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/nodes/Gain.litcoffee: -------------------------------------------------------------------------------- 1 | ## Gain 2 | 3 | Wraps the GainNode AudioContext object 4 | 5 | class Gain extends MooogAudioNode 6 | constructor: (@_instance, config = {}) -> 7 | super 8 | 9 | before_config: (config)-> 10 | @insert_node @context.createGain(), 0 11 | @_nodes[0].gain.value = @_instance.config.default_gain 12 | 13 | after_config: (config)-> 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/nodes/MediaElementSource.litcoffee: -------------------------------------------------------------------------------- 1 | ## MediaElementSource 2 | 3 | Wraps the MediaElementSourceNode AudioContext object. The `HTMLMediaElement` argument 4 | of the native constructor can be passed in the configuration object, either as such or 5 | via a CSS selector. 6 | 7 | class MediaElementSource extends MooogAudioNode 8 | constructor: (@_instance, config = {}) -> 9 | super 10 | 11 | before_config: (config)-> 12 | if !config.mediaElement 13 | throw new Error("MediaElementSource requires mediaElement config argument") 14 | if typeof(config.mediaElement) is 'string' 15 | config.mediaElement = document.querySelector( config.mediaElement ) 16 | @insert_node @context.createMediaElementSource( config.mediaElement ), 0 17 | 18 | after_config: (config)-> 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/nodes/Oscillator.litcoffee: -------------------------------------------------------------------------------- 1 | ## Oscillator 2 | 3 | Wraps the OscillatorNode AudioContext object 4 | 5 | class Oscillator extends MooogAudioNode 6 | constructor: (@_instance, config = {}) -> 7 | super 8 | 9 | before_config: (config)-> 10 | @insert_node @context.createOscillator(), 0 11 | 12 | after_config: (config)-> 13 | @insert_node new Gain( @_instance, { connect_to_destination: @config.connect_to_destination } ) 14 | @_nodes[1].gain.value = 1.0 15 | @_is_started = false 16 | @_state = 'stopped' 17 | @_timeout = false 18 | @define_readonly_property 'state', () => 19 | @_state 20 | 21 | start: (time = 0) -> 22 | clearTimeout @_timeout 23 | return @ if @_state is 'playing' 24 | if time is 0 25 | @__start(time) 26 | else 27 | @_timeout = setTimeout @__start, time * 1000 28 | @ 29 | 30 | stop: (time = 0) -> 31 | clearTimeout @_timeout 32 | return @ if @_state is 'stopped' 33 | if time is 0 34 | @__stop() 35 | else 36 | @_timeout = setTimeout @__stop, time * 1000 37 | @ 38 | 39 | __stop: () => 40 | @_state = 'stopped' 41 | @_nodes[1].gain.value = 0 42 | @ 43 | 44 | __start: () => 45 | @_state = 'playing' 46 | if @_is_started 47 | @_nodes[1].gain.value = 1.0 48 | else 49 | @_nodes[0].start(0) 50 | @_is_started = true 51 | @ 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/nodes/Panner.litcoffee: -------------------------------------------------------------------------------- 1 | ## Panner 2 | 3 | Wraps the PannerNode AudioContext object 4 | 5 | class Panner extends MooogAudioNode 6 | constructor: (@_instance, config = {}) -> 7 | super 8 | 9 | before_config: (config)-> 10 | @insert_node @context.createPanner(), 0 11 | 12 | after_config: (config)-> 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/nodes/ScriptProcessor.litcoffee: -------------------------------------------------------------------------------- 1 | ## ScriptProcessor 2 | 3 | Wraps the ChannelMergerNode AudioContext object. The native constructor arguments 4 | `numberOfInputChannels`, `numberOfOutputChannels` and `bufferSize` can be passed in the 5 | configuration object, as can the callback `onaudioprocess` function. 6 | 7 | class ScriptProcessor extends MooogAudioNode 8 | constructor: (@_instance, config = {}) -> 9 | @__bufferSize = if config.bufferSize? then config.bufferSize else null 10 | @__numberOfInputChannels = \ 11 | if config.numberOfInputChannels? then config.numberOfInputChannels else 2 12 | @__numberOfOuputChannels = \ 13 | if config.numberOfOuputChannels? then config.numberOfOuputChannels else 2 14 | delete config.bufferSize 15 | delete config.numberOfInputChannels 16 | delete config.numberOfOuputChannels 17 | @debug "ScriptProcessorNode is deprecated and will be replaced by AudioWorker" 18 | super 19 | 20 | 21 | 22 | before_config: (config)-> 23 | @insert_node @context.createScriptProcessor( 24 | @__bufferSize, @__numberOfInputChannels, @__numberOfOuputChannels 25 | ), 0 26 | 27 | after_config: (config)-> 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/nodes/StereoPanner.litcoffee: -------------------------------------------------------------------------------- 1 | ## StereoPanner 2 | 3 | Wraps the StereoPannerNode AudioContext object 4 | 5 | class StereoPanner extends MooogAudioNode 6 | constructor: (@_instance, config = {}) -> 7 | super 8 | 9 | before_config: (config)-> 10 | @insert_node @context.createStereoPanner(), 0 11 | 12 | after_config: (config)-> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/nodes/Track.litcoffee: -------------------------------------------------------------------------------- 1 | ## Track 2 | 3 | A convenience function that creates a chain with the specified node(s) 4 | followed by a panner/gain stage. It can also create pre/post sends with their 5 | own gain. The gain and pan `AudioParams` of those stages can be accessed 6 | via `gain` and `pan` methods exposed on the track object. Track objects can 7 | be the source or destination of `connect` and `chain` methods, just like any 8 | other node. 9 | 10 | 11 | class Track extends MooogAudioNode 12 | 13 | 14 | constructor: (@_instance, config = {}) -> 15 | @_sends = {} 16 | @debug 'initializing track object' 17 | config.node_type = 'Track' 18 | super 19 | 20 | before_config: (config)-> 21 | #todo: make sure we don't need to use insert_node() for this 22 | @_pan_stage = @_instance.context.createStereoPanner() 23 | @_gain_stage = @_instance.context.createGain() 24 | @_gain_stage.gain.value = @_instance.config.default_gain 25 | @_pan_stage.connect @_gain_stage 26 | @_gain_stage.connect @_destination 27 | @_destination = @_pan_stage 28 | @gain = @_gain_stage.gain 29 | @pan = @_pan_stage.pan 30 | 31 | after_config: (config) -> 32 | 33 | 34 | 35 | ### Track.send 36 | Creates a send to another `Track` object. 37 | `id`: String ID to assign to this send. 38 | `destination`: The target `Track` object or ID thereof 39 | `pre`: Either 'pre' or 'post'. *Defaults to config value in the `Mooog` object.* 40 | `gain`: Initial gain value for the send. *Defaults to config value in the `Mooog` object.* 41 | 42 | send: (id, 43 | dest, 44 | pre = @_instance.config.default_send_type, 45 | gain = @_instance.config.default_gain) -> 46 | return @_sends[id] unless dest? 47 | source = if (pre is 'pre') then @_nodes[@_nodes.length - 1] else @_gain_stage 48 | return @_sends[id] if @_sends[id]? 49 | @_sends[id] = new_send = new Gain @_instance, { connect_to_destination: false, gain: gain } 50 | source.connect @to new_send 51 | new_send.connect @to dest 52 | new_send 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/nodes/Waveshaper.litcoffee: -------------------------------------------------------------------------------- 1 | ## WaveShaper 2 | 3 | Wraps the WaveShaperNode AudioContext object 4 | 5 | class WaveShaper extends MooogAudioNode 6 | 7 | constructor: (@_instance, config = {}) -> 8 | super 9 | 10 | before_config: (config)-> 11 | @insert_node @context.createWaveShaper(), 0 12 | 13 | after_config: (config)-> 14 | 15 | 16 | 17 | 18 | 19 | chebyshev: (terms, last = [1], current = [1,0]) -> 20 | if terms < 2 21 | throw new Error("Terms must be 2 or more for chebyshev generator") 22 | if current.length is terms 23 | return @poly.apply @, current 24 | else 25 | lasttemp = last 26 | last = current 27 | current = current.map( (x)-> 2*x ) 28 | current.push 0 29 | lasttemp.unshift 0, 0 30 | lasttemp = lasttemp.map( (x) -> -1 * x ) 31 | newcurrent = for el, i in current 32 | lasttemp[i] + current[i] 33 | return @chebyshev terms, last, newcurrent 34 | 35 | # used by the chebyshev generator once the coefficients have been calculated 36 | poly: (coeffs...) -> 37 | length = @_instance.config.curve_length 38 | step = 2/(length - 1) 39 | curve = new Float32Array(length) 40 | p = (x, coeffs) -> 41 | accum = 0 42 | for i in [0..(coeffs.length-1)] 43 | a = coeffs[i] 44 | accum += (a * Math.pow(x, coeffs.length - i - 1)) 45 | accum 46 | 47 | curve[i] = ( p(((i * step) - 1), coeffs) ) for i in [0..length-1] 48 | curve 49 | 50 | 51 | 52 | 53 | tanh: (n) -> 54 | length = @_instance.config.curve_length 55 | step = 2 / (length - 1) 56 | curve = new Float32Array(length) 57 | curve[i] = ( 58 | Math.tanh((Math.PI / 2) * n * ((i * step) - 1)) 59 | ) for i in [0..length-1] 60 | curve 61 | 62 | 63 | 64 | 65 | 66 | --------------------------------------------------------------------------------