├── .babelrc
├── .editorconfig
├── .env.sample
├── .eslintrc.cjs
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── ask-a-question.md
│ ├── report-a-bug.md
│ └── suggest-a-new-feature.md
└── workflows
│ └── codeql-analysis.yml
├── .gitignore
├── .nycrc
├── BANNER.txt
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── SECURITY.md
├── examples
├── README.md
├── electron
│ ├── README.md
│ └── basic-example
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── main.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── preload.js
│ │ └── renderer.js
├── next.js
│ ├── README.md
│ └── basic-example
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── pages
│ │ └── index.js
│ │ └── public
│ │ ├── favicon.ico
│ │ └── vercel.svg
├── p5.js
│ ├── README.md
│ ├── basic-example
│ │ ├── index.html
│ │ ├── sketch.js
│ │ └── styles.css
│ └── querying-note-state
│ │ ├── index.html
│ │ ├── sketch.js
│ │ └── styles.css
├── quick-start
│ └── index.html
├── react
│ ├── README.md
│ └── basic-example
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── robots.txt
│ │ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── reportWebVitals.js
│ │ └── setupTests.js
└── typescript
│ ├── README.md
│ └── basic-nodejs-example
│ ├── index.ts
│ ├── package-lock.json
│ └── package.json
├── package-lock.json
├── package.json
├── scripts
├── api-documentation
│ ├── generate-html.js
│ ├── generate-markdown.js
│ └── templates
│ │ ├── core
│ │ ├── class.hbs
│ │ ├── constructor.hbs
│ │ ├── enumerations.hbs
│ │ ├── events.hbs
│ │ ├── methods.hbs
│ │ └── properties.hbs
│ │ └── helpers
│ │ ├── ddata.js
│ │ ├── djip-helpers.js
│ │ └── state.js
├── library
│ ├── build.js
│ ├── rollup.config.cjs.js
│ ├── rollup.config.cjs.min.js
│ ├── rollup.config.esm.js
│ ├── rollup.config.esm.min.js
│ ├── rollup.config.iife.js
│ └── rollup.config.iife.min.js
├── sponsors
│ └── retrieve-sponsors.js
├── typescript-declarations
│ ├── generate.js
│ └── generateOLD.js
└── website
│ └── deploy.js
├── src
├── Enumerations.js
├── Forwarder.js
├── Input.js
├── InputChannel.js
├── Message.js
├── Note.js
├── Output.js
├── OutputChannel.js
├── Utilities.js
└── WebMidi.js
├── test
├── Enumerations.test.js
├── Forwarder.test.js
├── Input.test.js
├── InputChannel.test.js
├── Message.test.js
├── Note.test.js
├── Output.test.js
├── OutputChannel.test.js
├── Utilities.test.js
├── WebMidi.test.js
└── support
│ ├── JZZ.js
│ ├── Utils.cjs.js
│ └── Utils.iife.js
├── typescript
└── webmidi.d.ts
└── website
├── .gitignore
├── README.md
├── api
├── classes
│ ├── Enumerations.md
│ ├── EventEmitter.md
│ ├── Forwarder.md
│ ├── Input.md
│ ├── InputChannel.md
│ ├── Listener.md
│ ├── Message.md
│ ├── Note.md
│ ├── Output.md
│ ├── OutputChannel.md
│ ├── Utilities.md
│ ├── WebMidi.md
│ └── _category_.json
└── index.md
├── babel.config.js
├── blog
└── 2021-12-01
│ ├── version-3-has-been-released.md
│ └── webmidi.js-is-available-now.png
├── docs
├── archives
│ ├── _category_.json
│ ├── v1.md
│ └── v2.md
├── getting-started
│ ├── _category_.json
│ ├── basics.md
│ ├── installation.md
│ └── supported-environments.md
├── going-further
│ ├── _category_.json
│ ├── electron.md
│ ├── forwarding.md
│ ├── middle-c.md
│ ├── performance.md
│ ├── sysex.md
│ └── typescript.md
├── index.md
├── migration
│ ├── _category_.json
│ └── migration.md
└── roadmap
│ ├── _category_.json
│ ├── under-evaluation.md
│ ├── v3.md
│ └── v4.md
├── docusaurus.config.js
├── package-lock.json
├── package.json
├── sidebars.js
├── src
├── components
│ ├── Button.js
│ ├── Button.module.css
│ ├── Button.module.css.map
│ ├── Button.module.scss
│ ├── Column.js
│ ├── Column.module.css
│ ├── Column.module.css.map
│ ├── Column.module.scss
│ ├── HomepageFeatures.js
│ ├── HomepageFeatures.module.css
│ ├── InformationBar.js
│ ├── InformationBar.module.css
│ ├── InformationBar.module.css.map
│ └── InformationBar.module.scss
├── css
│ ├── custom.css
│ ├── custom.css.map
│ ├── custom.scss
│ ├── index.css
│ ├── index.css.map
│ └── index.scss
├── pages
│ ├── about
│ │ └── index.md
│ ├── index.js
│ ├── index.module.css
│ ├── index.module.css.map
│ ├── index.module.scss
│ ├── research
│ │ └── index.md
│ ├── showcase
│ │ └── index.md
│ ├── sponsors
│ │ └── index.md
│ └── tester
│ │ └── index.js
└── theme
│ ├── CodeBlock
│ └── index.js
│ ├── Footer
│ ├── index.js
│ ├── styles.module.css
│ ├── styles.module.css.map
│ └── styles.module.scss
│ └── Navbar
│ ├── index.js
│ └── styles.module.css
└── static
├── .nojekyll
├── img
├── blog
│ ├── 2021-12-01
│ │ └── webmidijs-is-out.png
│ └── jean-philippe_cote.jpg
├── docusaurus.png
├── favicon.ico
├── front-page
│ ├── presentation-illustration-keyboard.svg
│ ├── webmidi-demonstration-vertical.svg
│ └── webmidi-demonstration.svg
├── logo.svg
├── og-card.png
├── person.png
├── sponsors
│ ├── edouard-montpetit-logo.svg
│ └── user.png
├── tutorial
│ ├── docsVersionDropdown.png
│ └── localeDropdown.png
├── undraw_docusaurus_mountain.svg
├── undraw_docusaurus_react.svg
├── undraw_docusaurus_tree.svg
├── webmidijs-logo-color-on-white.svg
├── webmidijs-logo-dark.svg
├── webmidijs-logo-light.svg
├── webmidijs-logo-small.png
├── webmidijs3-logo-1178x406.png
├── webmidijs3-logo-1280x640.png
└── webmidijs3-logo-40x40.png
├── js
└── newsletter-popup.js
└── styles
├── default.css
├── default.css.map
├── default.scss
└── jsdoc.css
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env", { "targets": {"node": "current"} }
5 | ]
6 | ],
7 | "env": {
8 | "test": {
9 | "plugins": [ "istanbul" ]
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps maintain consistent coding styles for multiple developers working on the same
2 | # project across various editors and IDEs: https://editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = lf
8 | insert_final_newline = true
9 | charset = utf-8
10 | indent_style = space
11 | indent_size = 2
12 | max_line_length = 100
13 | trim_trailing_whitespace = true
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
18 | [COMMIT_EDITMSG]
19 | max_line_length = 0
20 |
--------------------------------------------------------------------------------
/.env.sample:
--------------------------------------------------------------------------------
1 | # This is an example file. To actually use it, you need to rename it ".env". The .env file is used
2 | # by the dotenv module to set environment variables needed during development. The .env file MUST
3 | # NEVER BE committed.
4 |
5 | # Token used by the `release-it` module to create automatic GitHub releases.
6 | GITHUB_TOKEN=XXXXX
7 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |
3 | "env": {
4 | "amd": true,
5 | "browser": true,
6 | "mocha": true,
7 | "node": true,
8 | "es6": true
9 | },
10 |
11 | "parserOptions": {
12 | "ecmaVersion": "latest",
13 | "sourceType": "module"
14 | },
15 |
16 | "globals": {
17 | "Promise": "readonly",
18 | "WebMidi": "readonly",
19 | "chai": "readonly",
20 | "sinon": "readonly",
21 | "expect": "readonly",
22 | "Note": "readonly",
23 | "isNative": "readonly",
24 | "config": "readonly"
25 | },
26 |
27 | "extends": [
28 | "eslint:recommended",
29 | "prettier",
30 | "plugin:react/recommended"
31 | ],
32 |
33 | // The idea here is to stick to the rules defined by Prettier (https://prettier.io/) and only make
34 | // exceptions in ESLint when absolutely necessary.
35 | "rules": {
36 |
37 | // Rules to align ESLint with Prettier (even though we are already using eslint-config-prettier)
38 | "indent": ["error", 2],
39 | "semi": ["error", "always"],
40 | "quote-props": ["error", "as-needed"],
41 | "quotes": ["error", "double", {"avoidEscape": true, "allowTemplateLiterals": true}],
42 |
43 | // Rules that knowingly change the default Prettier behaviour
44 | "no-multi-spaces": ["error", { "ignoreEOLComments": true }],
45 | "linebreak-style": ["error", "unix"], // Force \n instead of Prettier's auto-detect behaviour
46 | "no-trailing-spaces": ["error", { "skipBlankLines": true, "ignoreComments": true }],
47 | "max-len": ["error", { "code": 100, "comments": 150 }], // Prettier's 80 is too small. Period.
48 | "no-console": ["error", { "allow": ["info", "warn", "error"] }], // Only some (unlike Prettier)
49 |
50 | // Other rules
51 | "no-prototype-builtins": "off",
52 |
53 | "react/prop-types": "off"
54 |
55 | },
56 |
57 | "settings": {
58 | "react": {
59 | "version": "detect"
60 | }
61 | }
62 |
63 | };
64 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # Up to 4 GitHub sponsors-enabled usernames e.g. [user1, user2]
2 | github: [djipco]
3 |
4 | # Single Patreon username
5 | #patreon:
6 |
7 | # Replace with a single Open Collective username
8 | #open_collective:
9 |
10 | # Replace with a single Ko-fi username
11 | #ko_fi:
12 |
13 | # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
14 | #tidelift:
15 |
16 | # Replace with a single Community Bridge project-name e.g., cloud-foundry
17 | #community_bridge:
18 |
19 | # Replace with a single Liberapay username
20 | #liberapay:
21 |
22 | # Replace with a single IssueHunt username
23 | #issuehunt:
24 |
25 | # Replace with a single Otechie username
26 | #otechie:
27 |
28 | # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
29 | #custom:
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/ask-a-question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Ask a question
3 | about: This is to ask a usage, support or general question
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Questions should be submitted to the Forum**
11 |
12 | All questions should be submitted to the **Questions & Support** section of the WebMidi.js Forum:
13 |
14 | https://webmidijs.org/forum/
15 |
16 | This opens up the question to the community while reserving GitHub strictly for bugs and issues.
17 |
18 | Thank you.
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/report-a-bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Report a bug
3 | about: This is only to report a bug, issue or problem.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Description**
11 | Describe the problem and how to reproduce it. If appropriate, include code samples, screenshots, error messages, etc.
12 |
13 | **Environment:**
14 | Specify the environment where you are witnessing the problem:
15 | - Library version and flavour (CJS, ESM or IIFE)
16 | - Runtime (browser or Node.js) and version
17 | - Language (JavaScript or TypeScript)
18 | - Operating system
19 |
20 | **Details**
21 | Add any other information, context or details that could help track down the problem.
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/suggest-a-new-feature.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Suggest a new feature
3 | about: This is to suggest a new feature or improvement
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Enhancement proposals should be submitted in the Forum**
11 |
12 | To submit a new feature or improvement request, please post it to the **Enhancement Proposals** section of the WebMidi.js Forum:
13 |
14 | https://webmidijs.org/forum/
15 |
16 | This allows the feature request to be discussed with the community while reserving GitHub strictly for bugs and issues.
17 |
18 | Thank you.
19 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '32 5 * * 4'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v1
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v1
71 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### System #########################################################################################
2 | .DS_Store*
3 | Icon?
4 | ._*
5 | Thumbs.db
6 | ehthumbs.db
7 | Desktop.ini
8 | .directory
9 | *~
10 | *.tgz
11 |
12 | ### Editors ########################################################################################
13 | .idea
14 |
15 | .vscode/*
16 | !.vscode/settings.json
17 | !.vscode/tasks.json
18 | !.vscode/launch.json
19 | !.vscode/extensions.json
20 |
21 | *.sublime-project
22 | *.sublime-workspace
23 |
24 | ### Dependency Directories #########################################################################
25 | node_modules
26 |
27 | ### Logs ###########################################################################################
28 | *.log
29 |
30 | ### Circle CI ######################################################################################
31 | .circleci
32 |
33 | ### Test coverage ##################################################################################
34 | .nyc_output
35 | coverage
36 |
37 | ### Passwords, tokens and such #####################################################################
38 | .credentials
39 | .env*
40 | !.env.sample
41 |
42 | ### Production build ###############################################################################
43 | dist
44 |
--------------------------------------------------------------------------------
/.nycrc:
--------------------------------------------------------------------------------
1 | {
2 | "report-dir": "./node_modules/nyc/.nyc_output",
3 | "temp-dir": "./node_modules/nyc/.coverage"
4 | }
5 |
--------------------------------------------------------------------------------
/BANNER.txt:
--------------------------------------------------------------------------------
1 | <%= pkg.webmidi.name %> v<%= pkg.version %>
2 | <%= pkg.webmidi.tagline %>
3 | <%= pkg.homepage %>
4 | Build generated on <%= moment().format('MMMM Do, YYYY') %>.
5 |
6 | © Copyright 2015-<%= moment().format('YYYY') %>, Jean-Philippe Côté.
7 |
8 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
9 | in compliance with the License. You may obtain a copy of the License at
10 |
11 | http://www.apache.org/licenses/LICENSE-2.0
12 |
13 | Unless required by applicable law or agreed to in writing, software distributed under the License
14 | is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
15 | or implied. See the License for the specific language governing permissions and limitations under
16 | the License.
17 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | First off, **thank you** for considering to contribute to this project. You should know that there
4 | are many ways to contribute. You can write tutorials or blog posts, improve the documentation,
5 | submit bug reports or feature requests and write actual source code. All of these are very
6 | worthwhile contributions.
7 |
8 | Following these guidelines helps to communicate that you respect the time of the developers managing
9 | this open source project. In return, they will reciprocate that respect in addressing your issue,
10 | assessing changes, and helping you finalize your pull requests.
11 |
12 | ## Submitting a Feature Requests
13 |
14 | If you find yourself wishing for a feature, you are probably not alone. There are bound to be others
15 | out there with similar needs. Before submitting a feature request, first check if the
16 | [wiki](https://github.com/djipco/webmidi/wiki)'s enhancements section already lists that feature.
17 |
18 | If not, open an [issue](https://github.com/djipco/webmidi/issues) which describes the feature you
19 | would like to see, why you need it, and how it should work.
20 |
21 | ## Understanding How to Contribute
22 |
23 | Contribution to this project is done via pull requests. This allows the owner and contributors to
24 | properly review what gets merged into the project.
25 |
26 | **If this is your first pull request**, you can learn how to get started from a free series of
27 | tutorials called
28 | [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
29 |
30 | ## Submitting a Pull Request
31 |
32 | **WebMidi.js** is a relatively small library supported by an even smaller team. Therefore, the
33 | process for contributing is intended to be simple and friendly.
34 |
35 | However, to insure good quality, there are steps that you should go through when submitting a PR.
36 | Here are the usual steps:
37 |
38 | 1. Discuss the change(s) you wish to make by means of an
39 | [issue](https://github.com/djipco/webmidi/issues).
40 | 2. Unless the PR is for a minor improvement (typo, documentation, etc.), you should write and/or
41 | update unit tests and check your code against the tests (see below).
42 | 3. If appropriate, update the [jsdoc](http://usejsdoc.org/) comments. Keeping the documentation and
43 | the API consistant is very important.
44 | 4. If appropriate, update the `README.md` file.
45 |
46 | Please note that your code should adhere to the styles defined in `.eslintrc.js`. You can use
47 | `npm run lint` to make sure it does.
48 |
49 | Finally, **do not** update the library's version number. Version numbering and releases will be
50 | handled by the owner. If the PR breaks backwards-compatibility, it must be communicated explicitely
51 | to the owner. The versioning scheme follows the [SemVer](http://semver.org/) standard.
52 |
53 | ## Testing
54 |
55 | WebMidi.js now has a proper test suite. The tests can be run on the command line without a need for
56 | a browser (thanks to Tim Susa).
57 |
58 | You can execute all tests, including code coverage, by running the following command in the
59 | terminal or on the command line:
60 |
61 | ```
62 | npm run test-all
63 | ```
64 |
65 | You can develop in *watch mode* with hot file reloading like so:
66 | ```
67 | npm run test -- -w
68 | ```
69 |
70 | You can start a single test in this way:
71 | ```
72 | npx mocha ./test/virtual-midi-test.js
73 | ```
74 |
75 | You can develop a single test in *watch mode* like this:
76 | ```
77 | npx mocha ./test/virtual-midi-test.js -- -w
78 | ```
79 |
80 | If you simply want to view code coverage, you can do:
81 | ```
82 | npm run test-coverage
83 | ```
84 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://www.jsdelivr.com/package/npm/webmidi)
4 | [](https://www.npmjs.com/package/webmidi)
5 | [](https://www.npmjs.com/package/webmidi)
6 | [](https://github.com/djipco/webmidi)
7 | [](https://www.npmjs.com/package/webmidi)
8 |
9 | ## Introduction
10 |
11 | **WEBMIDI.js** makes it easy to interact with MIDI instruments directly from a web browser or from
12 | Node.js. It simplifies the control of physical or virtual MIDI instruments with user-friendly
13 | functions such as `playNote()`, `sendPitchBend()` or `sendControlChange()`. It also allows reacting
14 | to inbound MIDI messages by adding listeners for events such as `"noteon"`, `"pitchbend"` or
15 | `"programchange"`.
16 |
17 | In short, the goal behind WEBMIDI.js is to get you started with your web-based MIDI project as quickly
18 | and efficiently as possible.
19 |
20 | ## Getting Started
21 |
22 | The [**official website**](https://webmidijs.org) site is the best place to get started. Over there,
23 | you will find, amongst others, two key resources:
24 |
25 | * [Documentation](https://webmidijs.org/docs/)
26 | * [API Reference](https://webmidijs.org/api/)
27 |
28 | To exchange with fellow users and myself, you can visit our [**Forum**](https://github.com/djipco/webmidi/discussions)
29 | which is hosted on the GitHub Discussions platform:
30 |
31 | * [Forum](https://github.com/djipco/webmidi/discussions)
32 |
33 | If you want to stay up-to-date, here are your best sources:
34 |
35 | * [Newsletter](https://mailchi.mp/eeffe50651bd/webmidijs-newsletter)
36 | * [Twitter](https://twitter.com/webmidijs)
37 |
38 | ## Sponsors
39 |
40 | WEBMIDI.js is a passion project but it still takes quite a bit of time, effort and money to develop and
41 | maintain. That's why I would like to sincerely thank 👏 these sponsors for their support:
42 |
43 | [ ](https://github.com/awatterott "@awatterott") [ ](https://github.com/rubendax "@rubendax") [ ](https://github.com/philmillman "@philmillman")
44 |
45 | If you use the library and find it useful, please 💜 [**sponsor the project**](https://github.com/sponsors/djipco).
46 |
47 | ## Feature Request
48 |
49 | If you would like to request a new feature, enhancement or API change, please first check that it is
50 | not [already planned](https://webmidijs.org/docs/future-versions/next). Then, discuss it in the
51 | [Enhancement Proposals](https://github.com/djipco/webmidi/discussions/categories/feature-requests)
52 | section of the forum.
53 |
54 | ## Citing this Software in Research
55 |
56 | If you use this software for research or academic purposes, please cite the project in your
57 | references (or wherever appropriate). Here's an example of how to cite it
58 | ([APA Style](https://apastyle.apa.org/)):
59 |
60 | >Côté, J. P. (2021). WebMidi.js v3.0.0 [Computer Software]. Retrieved from
61 | https://github.com/djipco/webmidi
62 |
63 | Cheers!
64 |
65 | -- Jean-Philippe
66 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 |
6 | | Version | Support | Notes |
7 | | ------------- | ------------------ | ------------------------------------------------------------- |
8 | | 3.0.x (alpha) | :white_check_mark: | This is the current, officially-supported version. |
9 | | 2.5.x | :white_check_mark: | Bug and security fixes only. |
10 | | 1.0.x | :x: | This version has reached end of life. |
11 |
12 | ## Reporting a Vulnerability
13 |
14 | To report a security-related problem, **you should not file an issue on GitHub** as this might put
15 | users at risk. **Instead, send an email to jp@cote.cc**.
16 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # WEBMIDI.js Usage Examples
2 |
3 | In this directory, you will find starter examples to help you use WEBMIDI.js in various contexts or
4 | for various purposes.
5 |
6 | ## Browser Quick Start Example
7 |
8 | * [Quick Start](quick-start/)
9 |
10 | ## Frameworks
11 |
12 | * [Next.js](next.js/)
13 | * [p5.js](p5.js/)
14 | * [React](react/)
15 |
16 | ## Environments
17 |
18 | * [Electron](electron/)
19 |
20 | ## Languages
21 |
22 | * [TypeScript](typescript/)
23 |
24 | ## More Examples wanted!
25 |
26 | To submit a new example, simply send a pull request and it will be quickly reviewed. Please create
27 | a README.md file to provide information regarding how the example should be used.
28 |
--------------------------------------------------------------------------------
/examples/electron/README.md:
--------------------------------------------------------------------------------
1 | # Using WEBMIDI.js with Electron
2 |
3 | A collection of examples to use WEBMIDI.js inside an Electron application.
4 |
5 | ## Examples
6 |
7 | * [**Basic Electron Example**](basic-example)
8 |
9 | ## Platform specific notes
10 |
11 | The permission requests must be properly handled in the main process before initializing WEBMIDI:
12 |
13 | ```javascript
14 | mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, callback, details) => {
15 | if (permission === 'midi' || permission === 'midiSysex') {
16 | callback(true);
17 | } else {
18 | callback(false);
19 | }
20 | })
21 |
22 | mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin) => {
23 | if (permission === 'midi' || permission === 'midiSysex') {
24 | return true;
25 | }
26 |
27 | return false;
28 | });
29 | ```
30 |
--------------------------------------------------------------------------------
/examples/electron/basic-example/README.md:
--------------------------------------------------------------------------------
1 | # Starter Template for Electron
2 |
3 | This is a minimal Electron application based on the [Quick Start Guide](https://electronjs.org/docs/latest/tutorial/quick-start) within the Electron documentation.
4 |
5 | ## To Use
6 |
7 | ```bash
8 | # Install dependencies
9 | npm install
10 |
11 | # Run the app
12 | npm start
13 | ```
14 |
--------------------------------------------------------------------------------
/examples/electron/basic-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 | WEBMIDI.js + Electron Demo
14 |
15 |
16 |
17 |
18 |
19 | WEBMIDI.js + Electron Demo
20 |
21 |
22 | Node.js ,
23 | Chromium ,
24 | Electron
25 |
26 |
27 | WEBMIDI.js
28 | MIDI Inputs:
29 | MIDI Outputs:
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/examples/electron/basic-example/main.js:
--------------------------------------------------------------------------------
1 | // Modules to control application life and create native browser window
2 | const {app, BrowserWindow} = require("electron");
3 | const path = require("path");
4 |
5 | function createWindow () {
6 |
7 | // Create the browser window.
8 | const mainWindow = new BrowserWindow({
9 | width: 800,
10 | height: 600,
11 | webPreferences: {
12 | preload: path.join(__dirname, "preload.js")
13 | }
14 | });
15 |
16 | // Respond affirmatively to requests for MIDI and MIDI SysEx permissions
17 | mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, callback, details) => {
18 | if (permission === 'midi' || permission === 'midiSysex') {
19 | callback(true);
20 | } else {
21 | callback(false);
22 | }
23 | })
24 |
25 | // Respond affirmatively to checks for MIDI and MIDI SysEx permissions
26 | mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin) => {
27 | if (permission === 'midi' || permission === 'midiSysex') {
28 | return true;
29 | }
30 |
31 | return false;
32 | });
33 |
34 | // and load the index.html of the app.
35 | mainWindow.loadFile("index.html");
36 |
37 | // Open dev tools
38 | // mainWindow.webContents.openDevTools();
39 |
40 | }
41 |
42 | // This method will be called when Electron has finished initialization and is ready to create
43 | // browser windows. Some APIs can only be used after this event occurs.
44 | app.whenReady().then(() => {
45 |
46 | createWindow();
47 |
48 | app.on("activate", function () {
49 | // On macOS it's common to re-create a window in the app when the dock icon is clicked and there
50 | // are no other windows open.
51 | if (BrowserWindow.getAllWindows().length === 0) createWindow();
52 | });
53 |
54 | });
55 |
56 | // Quit when all windows are closed, except on macOS. There, it's common for applications and their
57 | // menu bar to stay active until the user quits explicitly with Cmd + Q.
58 | app.on("window-all-closed", function () {
59 | if (process.platform !== "darwin") app.quit();
60 | });
61 |
62 | // In this file you can include the rest of your app's specific main process code. You can also put
63 | // them in separate files and require them here.
64 |
--------------------------------------------------------------------------------
/examples/electron/basic-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webmidijs-electron-demo",
3 | "version": "1.0.0",
4 | "description": "A minimal Electron application",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "electron ."
8 | },
9 | "license": "CC0-1.0",
10 | "devDependencies": {
11 | "electron": "^22.3.25"
12 | },
13 | "dependencies": {
14 | "webmidi": "latest"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/electron/basic-example/preload.js:
--------------------------------------------------------------------------------
1 | // All of the Node.js APIs are available in the preload process. It has the same sandbox as a Chrome
2 | // extension.
3 | window.addEventListener("DOMContentLoaded", () => {
4 |
5 | const replaceText = (selector, text) => {
6 | const element = document.getElementById(selector);
7 | if (element) element.innerText = text;
8 | };
9 |
10 | for (const type of ["chrome", "node", "electron"]) {
11 | replaceText(`${type}-version`, process.versions[type]);
12 | }
13 |
14 | });
15 |
--------------------------------------------------------------------------------
/examples/electron/basic-example/renderer.js:
--------------------------------------------------------------------------------
1 | // This file is required by the index.html file and will be executed in the renderer process for
2 | // that window. No Node.js APIs are available in this process because `nodeIntegration` is turned
3 | // off. Use `preload.js` to selectively enable features needed in the rendering process.
4 |
5 | import {WebMidi} from "./node_modules/webmidi/dist/esm/webmidi.esm.js";
6 |
7 | WebMidi.enable()
8 | .then(onEnabled)
9 | .catch(err => console.warning(err));
10 |
11 | function onEnabled() {
12 |
13 | document.getElementById("webmidi-version").innerHTML += `v${WebMidi.version}`;
14 |
15 | WebMidi.inputs.forEach(input => {
16 | document.getElementById("webmidi-inputs").innerHTML += `${input.name}, `;
17 | });
18 |
19 | WebMidi.outputs.forEach(output => {
20 | document.getElementById("webmidi-outputs").innerHTML += `${output.name}, `;
21 | });
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/examples/next.js/README.md:
--------------------------------------------------------------------------------
1 | # Using WEBMIDI.js with Next.js
2 |
3 | A collection of examples to use WEBMIDI.js with the Next.js framework.
4 |
5 | ## Examples
6 |
7 | * [**Basic Next.js Example**](basic-example)
8 |
--------------------------------------------------------------------------------
/examples/next.js/basic-example/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 |
21 | # debug
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | # local env files
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
--------------------------------------------------------------------------------
/examples/next.js/basic-example/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Starter Template for Next.js
4 |
5 | This is a starter template for [Learn Next.js](https://nextjs.org/learn).
6 |
7 | ## To Use
8 |
9 | ```bash
10 | # Install dependencies
11 | npm install
12 |
13 | # Run the app
14 | npm run dev
15 | ```
16 |
--------------------------------------------------------------------------------
/examples/next.js/basic-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "dev": "next dev",
5 | "build": "next build",
6 | "start": "next start"
7 | },
8 | "dependencies": {
9 | "next": "^14.2.26",
10 | "react": "^18.2.0",
11 | "react-dom": "^18.2.0",
12 | "webmidi": "latest"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/examples/next.js/basic-example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/examples/next.js/basic-example/public/favicon.ico
--------------------------------------------------------------------------------
/examples/next.js/basic-example/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/examples/p5.js/README.md:
--------------------------------------------------------------------------------
1 | # Using WEBMIDI.js with p5.js
2 |
3 | A collection of examples to use WEBMIDI.js inside the p5.js framework.
4 |
5 | ## Examples
6 |
7 | * [**Basic Example**](basic-example): drawing circles on canvas when **note on** MIDI event is
8 | received.
9 |
10 | * [**Querying note state**](querying-note-state): draw keyboard keys in color when MIDI keys
11 | presssed
12 |
--------------------------------------------------------------------------------
/examples/p5.js/basic-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | WebMidi.js + p5.js - Basic Example
11 |
12 |
13 |
14 | WebMidi.js + p5.js - Basic Example
15 | Randomly draw circles when a note on event is received on MIDI channel 1
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/p5.js/basic-example/sketch.js:
--------------------------------------------------------------------------------
1 | function setup() {
2 |
3 | createCanvas(window.innerWidth, window.innerHeight);
4 | noStroke();
5 |
6 | // Enable WebMidi.js and trigger the onWebMidiEnabled() function when ready.
7 | WebMidi.enable()
8 | .then(onWebMidiEnabled)
9 | .catch(err => alert(err));
10 |
11 | }
12 |
13 | function draw() {
14 |
15 | }
16 |
17 | function onWebMidiEnabled() {
18 |
19 | // Check if at least one MIDI input is detected. If not, display warning and quit.
20 | if (WebMidi.inputs.length < 1) {
21 | alert("No MIDI inputs detected.");
22 | return;
23 | }
24 |
25 | // Add a listener on all the MIDI inputs that are detected
26 | WebMidi.inputs.forEach(input => {
27 |
28 | // When a "note on" is received on MIDI channel 1, generate a random color start
29 | input.channels[1].addListener("noteon", function() {
30 | fill(random(255), random(255), random(255));
31 | circle(random(width), random(height), 100);
32 | });
33 |
34 | });
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/examples/p5.js/basic-example/styles.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | width: 100vw;
3 | height: 100vh;
4 | margin: 0;
5 | }
6 |
7 | main {
8 | position: absolute;
9 | top: 0;
10 | left: 0;
11 | width: 100vw;
12 | height: 100vh;
13 | }
14 |
15 | h1, p {
16 | margin: 0;
17 | padding: 16px 16px 0 16px;
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/examples/p5.js/querying-note-state/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | WebMidi.js + p5.js - Querying Note State
11 |
12 |
13 |
14 | WebMidi.js + p5.js - Querying Note State
15 | Draw colored rectangles when specific notes are pressed.
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/p5.js/querying-note-state/sketch.js:
--------------------------------------------------------------------------------
1 | let channel;
2 |
3 | async function setup() {
4 |
5 | // Enable WebMidi.js
6 | await WebMidi.enable();
7 |
8 | // Display available inputs in console (use the name to retrieve it)
9 | console.log(WebMidi.inputs);
10 | const input = WebMidi.getInputByName("MPK mini 3");
11 | channel = input.channels[1];
12 |
13 | // Create canvas
14 | createCanvas(500, 200);
15 |
16 | }
17 |
18 | function draw() {
19 |
20 | // Check if WebMidi is enabled and channel has been assigned before moving on
21 | if (!channel) return;
22 |
23 | // Draw blank keys
24 | for (let i = 0; i < 8; i++) {
25 |
26 | // Default fill is white
27 | fill("white");
28 |
29 | // Each key has its own color. When it's pressed, we draw it in color (instead of white)
30 | if (i === 0 && channel.getNoteState("C4")) {
31 | fill("yellow");
32 | } else if (i === 1 && channel.getNoteState("D4")) {
33 | fill("red");
34 | } else if (i === 2 && channel.getNoteState("E4")) {
35 | fill("pink");
36 | } else if (i === 3 && channel.getNoteState("F4")) {
37 | fill("orange");
38 | } else if (i === 4 && channel.getNoteState("G4")) {
39 | fill("purple");
40 | } else if (i === 5 && channel.getNoteState("A4")) {
41 | fill("green");
42 | } else if (i === 6 && channel.getNoteState("B4")) {
43 | fill("turquoise");
44 | } else if (i === 7 && channel.getNoteState("C5")) {
45 | fill("blue");
46 | }
47 |
48 | // Draw the keys
49 | rect(85 + i * 40, 50, 30, 100);
50 |
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/examples/p5.js/querying-note-state/styles.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | width: 100vw;
3 | height: 100vh;
4 | margin: 0;
5 | }
6 |
7 | h1, p {
8 | margin: 0;
9 | padding: 16px 16px 0 16px;
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/examples/quick-start/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | WebMidi.js Quick Start
9 |
10 |
11 |
12 |
40 |
41 |
42 |
43 |
44 | WebMidi.js Quick Start
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/examples/react/README.md:
--------------------------------------------------------------------------------
1 | # Using WEBMIDI.js with p5.js
2 |
3 | A collection of examples to use WEBMIDI.js inside the React framework.
4 |
5 | ## Examples
6 |
7 | * [**Basic Example**](basic-example)
8 |
--------------------------------------------------------------------------------
/examples/react/basic-example/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/examples/react/basic-example/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `yarn build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/examples/react/basic-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "basic-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.1",
7 | "@testing-library/react": "^12.1.2",
8 | "@testing-library/user-event": "^13.5.0",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "^5.0.0",
12 | "web-vitals": "^2.1.2",
13 | "webmidi": "latest"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": [
23 | "react-app",
24 | "react-app/jest"
25 | ]
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/react/basic-example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/examples/react/basic-example/public/favicon.ico
--------------------------------------------------------------------------------
/examples/react/basic-example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/examples/react/basic-example/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/examples/react/basic-example/public/logo192.png
--------------------------------------------------------------------------------
/examples/react/basic-example/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/examples/react/basic-example/public/logo512.png
--------------------------------------------------------------------------------
/examples/react/basic-example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/examples/react/basic-example/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/examples/react/basic-example/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/react/basic-example/src/App.js:
--------------------------------------------------------------------------------
1 | import logo from './logo.svg';
2 | import './App.css';
3 | import {WebMidi} from "webmidi";
4 |
5 | function App() {
6 |
7 | WebMidi.enable()
8 | .then(() => console.log(WebMidi.inputs))
9 | .catch(err => console.log(err));
10 |
11 | return (
12 |
28 | );
29 | }
30 |
31 | export default App;
32 |
--------------------------------------------------------------------------------
/examples/react/basic-example/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render( );
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/examples/react/basic-example/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/examples/react/basic-example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/examples/react/basic-example/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/react/basic-example/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/examples/react/basic-example/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/examples/typescript/README.md:
--------------------------------------------------------------------------------
1 | # Using WEBMIDI.js in a TypeScript project
2 |
3 | Version 3 of WEBMIDI.js officially supports TypeScript. Type declarations are available for the ESM
4 | (ECMAScript modules) and CJS (CommonJS, a.k.a. Node.js modules) flavours in the `/dist` directory.
5 |
6 | To use the example, `cd` into the example directory, install the necessary modules with
7 | `npm install` and write some TypeScript code. You can generate the final JavaScript code with
8 | `tsc myFile.ts`. This should yield a `myFile.js` which you can run with `node myFile.js`.
9 |
10 | ## Examples
11 |
12 | * [**Basic Example**](basic-nodejs-example): listing input and output ports and reacting to
13 | pressed keyboard keys.
14 |
--------------------------------------------------------------------------------
/examples/typescript/basic-nodejs-example/index.ts:
--------------------------------------------------------------------------------
1 | import {WebMidi} from "webmidi";
2 |
3 | async function start() {
4 |
5 | await WebMidi.enable();
6 |
7 | // List available inputs
8 | console.log("Available inputs: ");
9 |
10 | WebMidi.inputs.forEach(input => {
11 | console.log("\t" + input.name);
12 | input.addListener("noteon", e => console.log(e.note.identifier));
13 | });
14 |
15 | // List available outputs
16 | console.log("Available outputs: ");
17 |
18 | WebMidi.outputs.forEach(output => {
19 | console.log("\t" + output.name);
20 | });
21 |
22 | }
23 |
24 | start();
25 |
--------------------------------------------------------------------------------
/examples/typescript/basic-nodejs-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "typescript-basic-nidejs-example",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "devDependencies": {
6 | "typescript": "^4.5.4"
7 | },
8 | "dependencies": {
9 | "webmidi": "latest"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/scripts/api-documentation/generate-html.js:
--------------------------------------------------------------------------------
1 | // This script generates web-based API documentation files from jsdoc comments in the source code
2 | // and commits them to the '/archives/api' directory of the 'gh-pages' branch. This makes them
3 | // available at djipco.githib.io/webmidi/archives/api/v3
4 |
5 | // Modules
6 | const fs = require("fs-extra");
7 | const fsPromises = require("fs").promises;
8 | const git = require("simple-git")();
9 | const pkg = require("../../package.json");
10 | const moment = require("moment");
11 | const path = require("path");
12 | const os = require("os");
13 | const rimraf = require("@alexbinary/rimraf");
14 | const system = require("system-commands");
15 |
16 | // Some paths
17 | const CUSTOM_CSS = "../css/custom.css";
18 | const TARGET_BRANCH = "gh-pages";
19 | const TARGET_PATH = "./archives/api/v" + pkg.version.split(".")[0];
20 |
21 | // Google Analytics configuration
22 | const GA_CONFIG = {
23 | ua: "UA-162785934-1",
24 | domain: "https://djipco.github.io/webmidi"
25 | };
26 |
27 | // JSDoc configuration object to write as configuration file
28 | const config = {
29 |
30 | tags: {
31 | allowUnknownTags: true
32 | },
33 |
34 | // The opts property is for command-line arguments passed directly in the config file
35 | opts: {
36 | template: "./node_modules/foodoc/template"
37 | },
38 |
39 | // Source files
40 | source: {
41 | include: [
42 | "./src/Enumerations.js",
43 | "./src/Forwarder.js",
44 | "./src/Input.js",
45 | "./src/InputChannel.js",
46 | "./src/Message.js",
47 | "./src/Note.js",
48 | "./src/Output.js",
49 | "./src/OutputChannel.js",
50 | "./src/Utilities.js",
51 | "./src/WebMidi.js",
52 |
53 | "./node_modules/djipevents/src/djipevents.js"
54 | ]
55 | },
56 |
57 | sourceType: "module",
58 |
59 | plugins: [
60 | "plugins/markdown"
61 | ],
62 |
63 | // Configurations for the Foodoc template
64 | templates: {
65 |
66 | systemName: `${pkg.webmidi.name} API`,
67 | systemSummary: pkg.webmidi.tagline,
68 | // systemLogo: LOGO_PATH,
69 | systemColor: "#ffcf09",
70 |
71 | copyright: `© ${pkg.author.name} , ` +
72 | `2015-${new Date().getFullYear()}. ` +
73 | `${pkg.webmidi.name} v${pkg.version} is released under the ${pkg.license} license.`,
74 |
75 | navMembers: [
76 | {kind: "class", title: "Classes", summary: "All documented classes."},
77 | {kind: "external", title: "Externals", summary: "All documented external members."},
78 | // {kind: "global", title: "Globals", summary: "All documented globals."},
79 | {kind: "mixin", title: "Mixins", summary: "All documented mixins."},
80 | {kind: "interface", title: "Interfaces", summary: "All documented interfaces."},
81 | {kind: "module", title: "Modules", summary: "All documented modules."},
82 | {kind: "namespace", title: "Namespaces", summary: "All documented namespaces."},
83 | {kind: "tutorial", title: "Tutorials", summary: "All available tutorials."}
84 | ],
85 |
86 | analytics: GA_CONFIG,
87 |
88 | stylesheets: [
89 | CUSTOM_CSS
90 | ],
91 |
92 | dateFormat: "MMMM Do YYYY @ H:mm:ss",
93 | sort: "longname, linenum, version, since",
94 |
95 | collapseSymbols: true
96 |
97 | }
98 |
99 | };
100 |
101 | function log(message) {
102 | console.info("\x1b[32m", message, "\x1b[0m");
103 | }
104 |
105 | async function execute() {
106 |
107 | // Temporary target folder
108 | const TMP_SAVE_PATH = await fsPromises.mkdtemp(path.join(os.tmpdir(), "webmidi-api-doc-"));
109 | const CONF_PATH = TMP_SAVE_PATH + "/.jsdoc.json";
110 |
111 | // Write temporary configuration file
112 | fs.writeFileSync(CONF_PATH, JSON.stringify(config));
113 |
114 | // Prepare jsdoc command and generate documentation
115 | const cmd = "./node_modules/.bin/jsdoc " +
116 | `--configure ${CONF_PATH} ` +
117 | `--destination ${TMP_SAVE_PATH}`;
118 | await system(cmd);
119 | log(`Documentation temporarily generated in "${TMP_SAVE_PATH}"`);
120 |
121 | // Remove temporary configuration file
122 | await rimraf(CONF_PATH);
123 |
124 | // Here, we remove the index.html page and replace it with the list_class.html file
125 | await fs.copy(
126 | TMP_SAVE_PATH + "/list_class.html",
127 | TMP_SAVE_PATH + "/index.html",
128 | {overwrite: true}
129 | );
130 |
131 | // Get current branch (so we can come back to it later)
132 | let results = await git.branch();
133 | const ORIGINAL_BRANCH = results.current;
134 |
135 | // Switch to target branch
136 | log(`Switching from '${ORIGINAL_BRANCH}' branch to '${TARGET_BRANCH}' branch`);
137 | await git.checkout(TARGET_BRANCH);
138 |
139 | // Move dir to final destination and commit
140 | await fs.move(TMP_SAVE_PATH, TARGET_PATH, {overwrite: true});
141 | await git.add([TARGET_PATH]);
142 | await git.commit("Updated on: " + moment().format(), [TARGET_PATH]);
143 | await git.push();
144 | log(`Changes committed to ${TARGET_PATH} folder of '${TARGET_BRANCH}' branch`);
145 |
146 | // Come back to original branch
147 | log(`Switching back to '${ORIGINAL_BRANCH}' branch`);
148 | await git.checkout(ORIGINAL_BRANCH);
149 |
150 | }
151 |
152 | // Execute and catch errors if any (in red)
153 | execute().catch(error => console.error("\x1b[31m", "Error: " + error, "\x1b[0m"));
154 |
--------------------------------------------------------------------------------
/scripts/api-documentation/generate-markdown.js:
--------------------------------------------------------------------------------
1 | // Imports
2 | const djipHelpers = require("./templates/helpers/djip-helpers.js");
3 | const fs = require("fs-extra");
4 | const git = require("simple-git")();
5 | const Handlebars = require("handlebars");
6 | const jsdoc2md = require("jsdoc-to-markdown");
7 | const moment = require("moment");
8 | const path = require("path");
9 | const process = require("process");
10 |
11 | // Paths
12 | const ROOT_PATH = process.cwd();
13 | const SOURCE_DIR = path.resolve(ROOT_PATH, "src");
14 | const TARGET_PATH = path.join(process.cwd(), "website", "api", "classes");
15 | const TEMPLATE_DIR = path.resolve(ROOT_PATH, "scripts/api-documentation/templates/");
16 | const DJIPEVENTS = path.resolve(ROOT_PATH, "node_modules/djipevents/src/djipevents.js");
17 |
18 | // Register some Handlebars helpers
19 | Handlebars.registerHelper({
20 | eventName: djipHelpers.eventName,
21 | stripNewlines: djipHelpers.stripNewlines,
22 | inlineLinks: djipHelpers.inlineLinks,
23 | methodSignature: djipHelpers.methodSignature,
24 | eq: djipHelpers.eq,
25 | ne: djipHelpers.ne,
26 | lt: djipHelpers.lt,
27 | gt: djipHelpers.gt,
28 | lte: djipHelpers.lte,
29 | gte: djipHelpers.gte,
30 | and: djipHelpers.and,
31 | or: djipHelpers.or,
32 | curly: djipHelpers.curly,
33 | createEventAnchor: djipHelpers.createEventAnchor,
34 | });
35 |
36 | async function generate() {
37 |
38 | // Get source files list
39 | let files = await fs.readdir(SOURCE_DIR);
40 | files = files.map(file => path.resolve(SOURCE_DIR, file));
41 | files.push(DJIPEVENTS);
42 |
43 | // Compute JSON from JSDoc
44 | const data = jsdoc2md.getTemplateDataSync({files: files});
45 |
46 | // Build list of classes (explicitly adding Listener and EventEmitter)
47 | let classes = files.map(file => path.basename(file, ".js")).filter(file => file !== "djipevents");
48 | classes.push("Listener", "EventEmitter");
49 |
50 | // Parse each class file and save parsed output
51 | classes.forEach(filepath => {
52 |
53 | const basename = path.basename(filepath, ".js");
54 | const filtered = data.filter(x => x.memberof === basename || x.id === basename);
55 |
56 | // Save markdown files
57 | fs.writeFileSync(
58 | path.resolve(TARGET_PATH, `${basename}.md`),
59 | parseFile(filtered)
60 | );
61 | console.info(`Saved markdown file to ${basename}.md`);
62 |
63 | });
64 |
65 | // Commit generated files
66 | await git.add([TARGET_PATH]);
67 | await git.commit("Automatically generated on: " + moment().format(), [TARGET_PATH]);
68 | console.info(`Files in ${TARGET_PATH} committed to git`);
69 | await git.push();
70 | console.info(`Files pushed to remote`);
71 |
72 | }
73 |
74 | function parseFile(data) {
75 |
76 | let output = "";
77 | let hbs;
78 | let filtered;
79 |
80 | // Sort elements according to kind and then according to name
81 | const order = {class: 0, constructor: 1, function: 2, member: 3, event: 4, enum: 5, typedef: 6};
82 | data.sort((a, b) => {
83 | if (order[a.kind] === order[b.kind]) {
84 | return a.id.localeCompare(b.id);
85 | } else {
86 | return order[a.kind] < order[b.kind] ? -1 : 1;
87 | }
88 | });
89 |
90 | // Sort 'fires' if present
91 | data.forEach(element => {
92 | if (Array.isArray(element.fires)) element.fires.sort();
93 | });
94 |
95 | // Class
96 | filtered = data.filter(el => el.kind === "class")[0];
97 | hbs = fs.readFileSync(path.resolve(TEMPLATE_DIR, `core/class.hbs`), {encoding: "utf-8"});
98 | output += Handlebars.compile(hbs)(filtered);
99 |
100 | // Constructor
101 | filtered = data.filter(el => el.kind === "constructor")[0];
102 | hbs = fs.readFileSync(path.resolve(TEMPLATE_DIR, `core/constructor.hbs`), {encoding: "utf-8"});
103 | output += Handlebars.compile(hbs)(filtered);
104 |
105 | // Members
106 | filtered = data.filter(el => el.kind === "member" && el.access !== "private");
107 | hbs = fs.readFileSync(path.resolve(TEMPLATE_DIR, `core/properties.hbs`), {encoding: "utf-8"});
108 | output += Handlebars.compile(hbs)(filtered);
109 |
110 | // Methods
111 | filtered = data.filter(el => el.kind === "function" && el.access !== "private");
112 | hbs = fs.readFileSync(path.resolve(TEMPLATE_DIR, `core/methods.hbs`), {encoding: "utf-8"});
113 | output += Handlebars.compile(hbs)(filtered);
114 |
115 | // Events
116 | filtered = data.filter(el => el.kind === "event" && el.access !== "private");
117 | hbs = fs.readFileSync(path.resolve(TEMPLATE_DIR, `core/events.hbs`), {encoding: "utf-8"});
118 | output += Handlebars.compile(hbs)(filtered);
119 |
120 | // Enums
121 | filtered = data.filter(el => el.kind === "enum" && el.access !== "private");
122 | hbs = fs.readFileSync(path.resolve(TEMPLATE_DIR, `core/enumerations.hbs`), {encoding: "utf-8"});
123 | output += Handlebars.compile(hbs)(filtered);
124 |
125 | // Strip out links to Listener class
126 | // output = output.replaceAll("[**Listener**](Listener)", "`Listener`");
127 | // output = output.replaceAll("[Listener](Listener)", "`Listener`");
128 | // output = output.replaceAll("[**arguments**](Listener#arguments)", "`arguments`");
129 |
130 | return output;
131 |
132 | }
133 |
134 | generate().catch(error => console.error("\x1b[31m", "Error: " + error, "\x1b[0m"));
135 |
--------------------------------------------------------------------------------
/scripts/api-documentation/templates/core/class.hbs:
--------------------------------------------------------------------------------
1 | {{!
2 |
3 | This template creates the 'class' portion of the output (at the top). It relies on the following
4 | helpers:
5 |
6 | - inlineLinks
7 | - eq
8 | - eventName
9 |
10 | }}
11 |
12 | {{! Class name }}
13 | # {{name}}
14 |
15 | {{! Class description }}
16 | {{{inlineLinks description}}}
17 |
18 | {{! Since }}
19 | {{#if since}}
20 | **Since**: {{since}}
21 | {{/if}}
22 |
23 | {{! Extends }}
24 | {{#if augments}}
25 | **Extends**: {{#each augments}}[`{{this}}`]({{this}}){{#unless @last}}, {{/unless}}{{/each}}
26 |
27 | {{/if}}
28 |
29 | {{! Fires }}
30 | {{#if fires}}
31 | **Fires**: {{#each fires}}[`{{eventName this}}`](#event:{{eventName this}}){{#unless @last}}, {{/unless}}{{/each}}
32 | {{/if}}
33 |
--------------------------------------------------------------------------------
/scripts/api-documentation/templates/core/constructor.hbs:
--------------------------------------------------------------------------------
1 | {{!
2 |
3 | This template creates the 'constructor' portion of the output (below the class). It relies on the
4 | following helpers:
5 |
6 | - stripNewlines
7 |
8 | }}
9 | {{#if this}}
10 |
11 | {{! Constructor name }}
12 | ### `Constructor`
13 |
14 | {{! Description }}
15 | {{{inlineLinks description}}}
16 |
17 | {{! Parameters }}
18 | {{#if params}}
19 |
20 | **Parameters**
21 |
22 | > `new {{this.longname}}({{methodSignature this}})`
23 |
24 |
25 |
26 | | Parameter | Type | Default | Description |
27 | | ------------ | ------------ | ------------ | ------------ |
28 | {{#each params}}
29 | |{{#if this.optional}}[{{/if}}**`{{this.name}}`**{{#if this.optional}}]{{/if}} | {{#each this.type.names}}{{this}} {{/each}} |{{this.defaultvalue}}|{{{stripNewlines (inlineLinks this.description)}}}|
30 | {{/each}}
31 |
32 |
33 |
34 | {{/if}}
35 |
36 | {{! Exceptions }}
37 | {{#if this.exceptions}}
38 | **Throws**:
39 | {{#each this.exceptions}}
40 | * {{#if this.type}}`{{this.type.names.[0]}}` : {{/if}}{{inlineLinks this.description}}
41 | {{/each}}
42 | {{/if}}
43 |
44 | {{/if}}
45 |
--------------------------------------------------------------------------------
/scripts/api-documentation/templates/core/enumerations.hbs:
--------------------------------------------------------------------------------
1 | {{!
2 |
3 | This template creates the 'enums' portion of the output . It relies on the following helpers:
4 |
5 | - inlineLinks
6 | - stripNewlines
7 |
8 | }}
9 | {{#if this}}
10 | ***
11 |
12 | ## Enums
13 |
14 | {{#each this}}
15 | {{! Name }}
16 | ### `.{{this.name}}` {{curly true}}#{{this.name}}{{curly}}
17 | **Type**: {{this.type.names.[0]}}
18 | {{! Attributes }}
19 | {{#if (eq this.scope "static")}}
20 | **Attributes**: static
21 | {{/if}}
22 |
23 | {{! Description }}
24 | {{{inlineLinks description}}}
25 | {{/each}}
26 |
27 | {{/if}}
28 |
--------------------------------------------------------------------------------
/scripts/api-documentation/templates/core/events.hbs:
--------------------------------------------------------------------------------
1 | {{!
2 |
3 | This template creates the 'Events' portion of the output . It relies on the following helpers:
4 |
5 | - inlineLinks
6 | - stripNewlines
7 |
8 | }}
9 | {{#if this}}
10 | ***
11 |
12 | ## Events
13 |
14 | {{#each this}}
15 | {{! Name and anchor }}
16 | ### `{{this.name}}` {{curly true}}{{createEventAnchor this.name}}{{curly}}
17 |
18 |
19 |
20 |
21 | {{! Description }}
22 | {{{inlineLinks description}}}
23 |
24 | {{! Since }}
25 | {{#if since}}
26 | **Since**: {{since}}
27 | {{/if}}
28 |
29 | {{! Properties }}
30 | {{#if properties}}
31 |
32 | **Event Properties**
33 |
34 | | Property | Type | Description |
35 | | ------------------------ | ------------------------ | ------------------------ |
36 | {{#each properties}}
37 | |**`{{this.name}}`** |{{this.type.names.[0]}}|{{{stripNewlines (inlineLinks this.description)}}}|
38 | {{/each}}
39 |
40 | {{/if}}
41 |
42 | {{/each}}
43 |
44 | {{/if}}
45 |
--------------------------------------------------------------------------------
/scripts/api-documentation/templates/core/methods.hbs:
--------------------------------------------------------------------------------
1 | {{!
2 |
3 | This template creates the 'Events' portion of the output . It relies on the following helpers:
4 |
5 | - inlineLinks
6 | - stripNewlines
7 |
8 | }}
9 | {{#if this}}
10 | ***
11 |
12 | ## Methods
13 |
14 | {{#each this}}
15 |
16 | {{! Name }}
17 | ### `.{{this.name}}({{#if params}}...{{/if}})` {{curly true}}#{{this.name}}{{curly}}
18 |
19 | {{! Since }}
20 | {{#if this.since}}
21 | **Since**: {{this.since}}
22 | {{/if}}
23 | {{! Attributes }}
24 | {{#if (eq this.async true)}}
25 | **Attributes**: async
26 | {{/if}}
27 |
28 | {{! Description }}
29 | {{{inlineLinks description}}}
30 |
31 | {{! Parameters }}
32 | {{#if params}}
33 |
34 | **Parameters**
35 |
36 | > Signature: `{{this.name}}({{methodSignature this}})`
37 |
38 |
39 |
40 | | Parameter | Type(s) | Default | Description |
41 | | ------------ | ------------ | ------------ | ------------ |
42 | {{#each params}}
43 | |{{#if this.optional}}[{{/if}}**`{{this.name}}`**{{#if this.optional}}]{{/if}} | {{#each this.type.names}}{{this}} {{/each}} |{{this.defaultvalue}}|{{{stripNewlines (inlineLinks this.description)}}}|
44 | {{/each}}
45 |
46 |
47 |
48 | {{/if}}
49 |
50 | {{! Returns }}
51 | {{#if returns}}
52 | **Return Value**
53 |
54 | {{! Return value description }}
55 | > Returns: {{#each returns~}}
56 | {{#if type~}}
57 | {{#each type.names~}}
58 | `{{{this}}}`{{#unless @last}} or {{/unless}}
59 | {{~/each}}
60 | {{/if~}}
61 |
62 | {{~#if description}}
63 |
64 | {{{inlineLinks description}}}
65 | {{/if~}}
66 | {{~/each}}
67 | {{/if}}
68 |
69 | {{! Attributes }}
70 | {{#if (eq this.scope "static")}}
71 |
72 | **Attributes**: static
73 | {{/if}}
74 |
75 | {{#if this.exceptions}}
76 | **Throws**:
77 | {{#each this.exceptions}}
78 | * {{#if this.type}}`{{this.type.names.[0]}}` : {{/if}}{{{inlineLinks this.description}}}
79 | {{/each}}
80 | {{/if}}
81 |
82 | {{/each}}
83 |
84 | {{/if}}
85 |
--------------------------------------------------------------------------------
/scripts/api-documentation/templates/core/properties.hbs:
--------------------------------------------------------------------------------
1 | {{!
2 |
3 | This template creates the 'properties' portion of the output . It relies on the following helpers:
4 |
5 | - inlineLinks
6 | - stripNewlines
7 |
8 | }}
9 | {{#if this}}
10 | ***
11 |
12 | ## Properties
13 |
14 | {{#each this}}
15 | {{! Name }}
16 | ### `.{{this.name}}` {{curly true}}#{{this.name}}{{curly}}
17 | {{! Since }}
18 | {{#if this.since}}
19 | **Since**: {{this.since}}
20 | {{/if}}
21 | {{! Type }}
22 | **Type**: {{this.type.names.[0]}}
23 | {{#if (or this.readonly this.nullable)}}
24 | {{! Attributes }}
25 | **Attributes**: {{#if this.readonly}}read-only{{/if}}{{#if this.nullable}}, nullable{{/if}}{{#if (eq this.scope "static")}}, static{{/if}}
26 | {{/if}}
27 |
28 |
29 | {{! Description }}
30 | {{{inlineLinks description}}}
31 |
32 | {{! Properties }}
33 | {{#if properties}}
34 |
35 | **Properties**
36 |
37 | | Property | Type | Description |
38 | | ------------ | ------------ | ------------ |
39 | {{#each properties}}
40 | |**`{{this.name}}`** |{{this.type.names.[0]}}|{{{stripNewlines (inlineLinks this.description)}}}|
41 | {{/each}}
42 |
43 | {{/if}}
44 |
45 | {{/each}}
46 |
47 | {{/if}}
48 |
--------------------------------------------------------------------------------
/scripts/api-documentation/templates/helpers/djip-helpers.js:
--------------------------------------------------------------------------------
1 | const ddata = require("./ddata.js");
2 |
3 | /**
4 | * Strips newline characters (\n) from the input
5 | * @param input {string}
6 | * @returns {string}
7 | */
8 | function stripNewlines (input) {
9 | if (input) return input.replace(/[\r\n]+/g, " ");
10 | }
11 | exports.stripNewlines = stripNewlines;
12 |
13 | /**
14 | * Extract the event name from the jsdoc-reported name
15 | * @param input {string}
16 | * @returns {string}
17 | */
18 | function eventName (input) {
19 | return input.split(":")[1];
20 | }
21 | exports.eventName = eventName;
22 |
23 | /**
24 | * Replaces JSDoc {@link} tags with markdown links in the supplied text
25 | */
26 | function inlineLinks (text, options) {
27 |
28 | if (text) {
29 | const links = ddata.parseLink(text);
30 | links.forEach(function (link) {
31 | const linked = ddata._link(link.url, options);
32 | if (link.caption === link.url) link.caption = linked.name;
33 | if (linked.url) link.url = linked.url;
34 | text = text.replace(link.original, `[${link.caption}](${link.url})`);
35 | });
36 | }
37 |
38 | return text;
39 |
40 | }
41 | exports.inlineLinks = inlineLinks;
42 |
43 | function curly(object, open) {
44 | return open ? "{" : "}";
45 | };
46 | exports.curly = curly;
47 |
48 | function eq(v1, v2) { return v1 === v2; }
49 | exports.eq = eq;
50 | function ne(v1, v2) { return v1 !== v2; }
51 | exports.ne = ne;
52 | function lt(v1, v2) {return v1 < v2; }
53 | exports.lt = lt;
54 | function gt(v1, v2) { return v1 > v2; }
55 | exports.gt = gt;
56 | function lte(v1, v2) { return v1 <= v2; }
57 | exports.lte = lte;
58 | function gte(v1, v2) { return v1 >= v2; }
59 | exports.gte = gte;
60 | function and() { return Array.prototype.every.call(arguments, Boolean); }
61 | exports.and = and;
62 | function or() { return Array.prototype.slice.call(arguments, 0, -1).some(Boolean); }
63 | exports.or = or;
64 |
65 | function methodSignature(context) {
66 | return ddata.methodSig.call(context);
67 | }
68 | exports.methodSignature = methodSignature;
69 |
70 | function createEventAnchor(name) {
71 | return "#event-" + name.replace(":", "-");
72 | }
73 | exports.createEventAnchor = createEventAnchor;
74 |
--------------------------------------------------------------------------------
/scripts/api-documentation/templates/helpers/state.js:
--------------------------------------------------------------------------------
1 | exports.templateData = []
2 | exports.options = {}
3 |
--------------------------------------------------------------------------------
/scripts/library/build.js:
--------------------------------------------------------------------------------
1 | // This script builds the bundled library files (both normal and minified with sourcemaps) and
2 | // commits them to the 'dist' folder for later publishing. By default, CommonJS, ES Modules and IIFE
3 | // versions are built.
4 | //
5 | // Calling this script with the -t argument allows building only of them. Options are: cjs, esm and
6 | // iife.
7 |
8 | // Modules
9 | const system = require("system-commands");
10 | const fs = require("fs");
11 | const path = require("path");
12 |
13 | // Parse arguments (default type is esm). Use -t as type (if valid)
14 | let type = "esm";
15 | const argv = require("minimist")(process.argv.slice(2));
16 | if (["cjs", "esm", "iife"].includes(argv.t)) type = argv.t;
17 |
18 | // Prepare general command
19 | let cmd = `./node_modules/.bin/rollup ` +
20 | `--input src/WebMidi.js ` +
21 | `--format ${type} `;
22 |
23 | // Minified version (with sourcemap)
24 | let minified = cmd + ` --file dist/${type}/webmidi.${type}.min.js ` +
25 | `--sourcemap ` +
26 | `--config ${__dirname}/rollup.config.${type}.min.js`;
27 |
28 | // Non-minified version
29 | let normal = cmd + ` --file dist/${type}/webmidi.${type}.js ` +
30 | `--config ${__dirname}/rollup.config.${type}.js`;
31 |
32 | async function execute(type) {
33 |
34 | // Write package.json file so proper versions are imported by Node
35 | if (type === "esm") {
36 |
37 | fs.writeFileSync(
38 | path.join(process.cwd(), "dist", "esm", "package.json"), '{"type": "module"}'
39 | );
40 |
41 | console.info(
42 | "\x1b[32m", // green font
43 | `Custom package.json file ("type": "module") saved to "dist/${type}/package.json"`,
44 | "\x1b[0m" // reset font
45 | );
46 |
47 | } else if (type === "cjs") {
48 |
49 | fs.writeFileSync(
50 | path.join(process.cwd(), "dist", "cjs", "package.json"), '{"type": "commonjs"}'
51 | );
52 |
53 | console.info(
54 | "\x1b[32m", // green font
55 | `Custom package.json file ("type": "commonjs") saved to "dist/${type}/package.json"`,
56 | "\x1b[0m" // reset font
57 | );
58 |
59 | }
60 |
61 | // Production build
62 | await system(minified);
63 |
64 | console.info(
65 | "\x1b[32m", // green font
66 | `The "${type}" minified build was saved to "dist/${type}/webmidi.${type}.min.js"`,
67 | "\x1b[0m" // reset font
68 | );
69 |
70 | // Development build
71 | await system(normal);
72 |
73 | console.info(
74 | "\x1b[32m", // green font
75 | `The "${type}" non-minified build was saved to "dist/${type}/webmidi.${type}.js"`,
76 | "\x1b[0m" // reset font
77 | );
78 | }
79 |
80 | // Execute and catch errors if any (in red)
81 | execute(type).catch(error => console.error("\x1b[31m", "Error: " + error, "\x1b[0m"));
82 |
83 |
--------------------------------------------------------------------------------
/scripts/library/rollup.config.cjs.js:
--------------------------------------------------------------------------------
1 | import babel from "@rollup/plugin-babel";
2 | import stripCode from "rollup-plugin-strip-code";
3 | import replace from "@rollup/plugin-replace";
4 | const fs = require("fs");
5 | const license = require("rollup-plugin-license");
6 | const versionInjector = require("rollup-plugin-version-injector");
7 |
8 | const BANNER = fs.readFileSync(__dirname + "/../../BANNER.txt", "utf8") + "\n\n\n";
9 |
10 | export default {
11 | plugins: [
12 | versionInjector(),
13 | replace({__flavour__: "cjs"}),
14 | stripCode({
15 | start_comment: "START-ESM",
16 | end_comment: "END-ESM"
17 | }),
18 | babel(),
19 | license({banner: BANNER})
20 | ]
21 | };
22 |
--------------------------------------------------------------------------------
/scripts/library/rollup.config.cjs.min.js:
--------------------------------------------------------------------------------
1 | import babel from "@rollup/plugin-babel";
2 | import replace from "@rollup/plugin-replace";
3 | const fs = require("fs");
4 | const license = require("rollup-plugin-license");
5 | import {terser} from "rollup-plugin-terser";
6 | import stripCode from "rollup-plugin-strip-code";
7 | const versionInjector = require("rollup-plugin-version-injector");
8 |
9 | const BANNER = fs.readFileSync(__dirname + "/../../BANNER.txt", "utf8") + "\n\n\n";
10 |
11 | export default {
12 | plugins: [
13 | versionInjector(),
14 | replace({__flavour__: "cjs"}),
15 | stripCode({
16 | start_comment: "START-ESM",
17 | end_comment: "END-ESM"
18 | }),
19 | babel(),
20 | terser(),
21 | license({banner: BANNER}),
22 | ]
23 | };
24 |
--------------------------------------------------------------------------------
/scripts/library/rollup.config.esm.js:
--------------------------------------------------------------------------------
1 | import stripCode from "rollup-plugin-strip-code";
2 | import replace from "@rollup/plugin-replace";
3 |
4 | const fs = require("fs");
5 | const license = require("rollup-plugin-license");
6 | const versionInjector = require("rollup-plugin-version-injector");
7 |
8 | const BANNER = fs.readFileSync(__dirname + "/../../BANNER.txt", "utf8") + "\n\n\n";
9 |
10 | export default {
11 |
12 | plugins: [
13 | versionInjector(),
14 | replace({__flavour__: "esm"}),
15 | stripCode({
16 | start_comment: "START-CJS",
17 | end_comment: "END-CJS"
18 | }),
19 | license({
20 | banner: BANNER
21 | })
22 | ]
23 |
24 | };
25 |
--------------------------------------------------------------------------------
/scripts/library/rollup.config.esm.min.js:
--------------------------------------------------------------------------------
1 | import stripCode from "rollup-plugin-strip-code";
2 | import replace from "@rollup/plugin-replace";
3 |
4 | const fs = require("fs");
5 | const license = require("rollup-plugin-license");
6 | import { terser } from "rollup-plugin-terser";
7 | const versionInjector = require("rollup-plugin-version-injector");
8 |
9 | const BANNER = fs.readFileSync(__dirname + "/../../BANNER.txt", "utf8") + "\n\n\n";
10 |
11 | export default {
12 |
13 | plugins: [
14 | versionInjector(),
15 | replace({__flavour__: "cjs"}),
16 | stripCode({
17 | start_comment: "START-CJS",
18 | end_comment: "END-CJS"
19 | }),
20 | terser(),
21 | license({banner: BANNER}),
22 | ]
23 |
24 | };
25 |
--------------------------------------------------------------------------------
/scripts/library/rollup.config.iife.js:
--------------------------------------------------------------------------------
1 | import babel from "@rollup/plugin-babel";
2 | import stripCode from "rollup-plugin-strip-code";
3 | import replace from "@rollup/plugin-replace";
4 |
5 | const fs = require("fs");
6 | const license = require("rollup-plugin-license");
7 | const versionInjector = require("rollup-plugin-version-injector");
8 |
9 | const BANNER = fs.readFileSync(__dirname + "/../../BANNER.txt", "utf8") + "\n\n\n";
10 |
11 | export default {
12 |
13 | output: {
14 | name: "window", // WebMidi and Note will be added to window
15 | extend: true, // important!
16 | exports: "named"
17 | },
18 |
19 | plugins: [
20 | versionInjector(),
21 | replace({__flavour__: "iife"}),
22 | stripCode({
23 | start_comment: "START-CJS",
24 | end_comment: "END-CJS"
25 | }),
26 | stripCode({
27 | start_comment: "START-ESM",
28 | end_comment: "END-ESM"
29 | }),
30 | babel(),
31 | license({
32 | banner: BANNER
33 | })
34 | ]
35 |
36 | };
37 |
--------------------------------------------------------------------------------
/scripts/library/rollup.config.iife.min.js:
--------------------------------------------------------------------------------
1 | import babel from "@rollup/plugin-babel";
2 | import stripCode from "rollup-plugin-strip-code";
3 | import replace from "@rollup/plugin-replace";
4 | import {terser} from "rollup-plugin-terser";
5 |
6 | const fs = require("fs");
7 | const license = require("rollup-plugin-license");
8 | const versionInjector = require("rollup-plugin-version-injector");
9 |
10 | const BANNER = fs.readFileSync(__dirname + "/../../BANNER.txt", "utf8") + "\n\n\n";
11 |
12 | export default {
13 |
14 | output: {
15 | name: "window", // WebMidi, Note and others will be added to window
16 | extend: true, // important!
17 | exports: "named"
18 | },
19 |
20 | plugins: [
21 | versionInjector(),
22 | replace({__flavour__: "cjs"}),
23 | stripCode({
24 | start_comment: "START-CJS",
25 | end_comment: "END-CJS"
26 | }),
27 | stripCode({
28 | start_comment: "START-ESM",
29 | end_comment: "END-ESM"
30 | }),
31 | babel(),
32 | terser(),
33 | license({banner: BANNER})
34 | ]
35 |
36 | };
37 |
--------------------------------------------------------------------------------
/scripts/sponsors/retrieve-sponsors.js:
--------------------------------------------------------------------------------
1 | const { graphql } = require("@octokit/graphql");
2 | const { token } = require("../../.credentials/sponsors.js");
3 | const replace = require("replace-in-file");
4 | const path = require("path");
5 |
6 | const TARGET = path.join(process.cwd(), "website", "src", "pages", "sponsors", "index.md");
7 |
8 | async function getSponsors() {
9 |
10 | const query = `{
11 |
12 | user (login: "djipco") {
13 | sponsorshipsAsMaintainer(first: 100, includePrivate: true) {
14 | totalCount
15 | nodes {
16 | sponsorEntity {
17 | ... on User {
18 | login
19 | name
20 | avatarUrl
21 | url
22 | sponsorshipForViewerAsSponsorable {
23 | isOneTimePayment
24 | privacyLevel
25 | tier {
26 | id
27 | name
28 | monthlyPriceInDollars
29 | }
30 | }
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
37 | }`;
38 |
39 | const {user} = await graphql(
40 | query,
41 | { headers: { authorization: `token ${token}` } }
42 | );
43 |
44 | return user.sponsorshipsAsMaintainer.nodes;
45 |
46 | }
47 |
48 | getSponsors().then(async data => {
49 |
50 | // data.forEach(d => {
51 | // console.log(d.sponsorEntity.login, d.sponsorEntity.sponsorshipForViewerAsSponsorable);
52 | // // console.log(d);
53 | // });
54 |
55 | let output = "";
56 |
57 | data.sort((a, b) => {
58 | a = a.sponsorEntity.sponsorshipForViewerAsSponsorable.tier.monthlyPriceInDollars;
59 | b = b.sponsorEntity.sponsorshipForViewerAsSponsorable.tier.monthlyPriceInDollars;
60 |
61 | if (a > b) return -1;
62 | if (a < b) return -1;
63 | return 0;
64 |
65 | });
66 |
67 | data.forEach(entity => {
68 |
69 | const sponsor = entity.sponsorEntity;
70 |
71 | if (sponsor.sponsorshipForViewerAsSponsorable.tier.monthlyPriceInDollars < 6) {
72 | return;
73 | }
74 |
75 | if (sponsor.sponsorshipForViewerAsSponsorable.privacyLevel === "PUBLIC") {
76 | const name = sponsor.name || sponsor.login;
77 | output += `\n`;
78 | output += `\t \n`;
80 | output += ` \n\n`;
81 | } else {
82 | output += ` \n\n`;
84 | }
85 |
86 | });
87 |
88 | const options = {
89 | files: TARGET,
90 | from: /.*/sgm,
91 | to: "\n\n" + output + "",
92 | };
93 |
94 | const results = await replace(options);
95 |
96 | });
97 |
--------------------------------------------------------------------------------
/scripts/typescript-declarations/generate.js:
--------------------------------------------------------------------------------
1 | // This script injects a few dynamic properties into the source TypeScript declaration file and
2 | // copies it to the ./dist/esm and ./dist/cjs directories.
3 |
4 | // Modules
5 | const path = require("path");
6 | const replace = require("replace-in-file");
7 | const pkg = require("../../package.json");
8 | const fs = require("fs-extra");
9 |
10 | // Path to source declaration file
11 | const SOURCE_FILE = path.join(__dirname, "../../typescript", "webmidi.d.ts");
12 |
13 | // Output directory
14 | const OUTPUT_DIR = "dist";
15 |
16 | // Console arguments
17 | const argv = require("minimist")(process.argv.slice(2));
18 |
19 | // Get targets to save the file for
20 | let targets = [];
21 | let type = "all";
22 | if (["cjs", "esm"].includes(argv.t)) type = argv.t;
23 |
24 | if (type === "all" || type === "cjs")
25 | targets.push({path: "webmidi.cjs.d.ts", name: "cjs", type: "CommonJS"});
26 |
27 | if (type === "all" || type === "esm")
28 | targets.push({path: "webmidi.esm.d.ts", name: "esm", type: "ES2020"});
29 |
30 | async function execute() {
31 |
32 | targets.forEach(async target => {
33 |
34 | const FILE = path.join(OUTPUT_DIR, target.name, target.path);
35 |
36 | // Copy the file to the target directory
37 | await fs.copy(SOURCE_FILE, FILE, {overwrite: true});
38 |
39 | // Insert dynamic data
40 | replace.sync({
41 | files: FILE,
42 | from: ["{{LIBRARY}}", "{{VERSION}}", "{{HOMEPAGE}}", "{{AUTHOR_NAME}}", "{{AUTHOR_URL}}"],
43 | to: [pkg.webmidi.name, pkg.version, pkg.homepage, pkg.author.name, pkg.author.url]
44 | });
45 |
46 | log(`Saved ${target.type} TypeScript declaration file to '${FILE}'`);
47 |
48 | });
49 |
50 | }
51 |
52 | // Execute and catch errors if any (in red)
53 | execute().catch(error => console.error("\x1b[31m", "Error: " + error, "\x1b[0m"));
54 |
55 | function log(message) {
56 | console.info("\x1b[32m", message, "\x1b[0m");
57 | }
58 |
--------------------------------------------------------------------------------
/scripts/typescript-declarations/generateOLD.js:
--------------------------------------------------------------------------------
1 | // This script generates TypeScript declaration files (.d.ts) of the library and saves them to the
2 | // 'dist' directory for later publishing. By default, CommonJS and ES2020 versions are generated.
3 | //
4 | // Calling this script with the -t argument allows generating only one declaration file. Options
5 | // are: cjs and esm.
6 | //
7 | // Calling this script with the -c argument allows you to commit and push the generated files.
8 | // Options are true or false.
9 |
10 | // Modules
11 | const git = require("simple-git")();
12 | const moment = require("moment");
13 | const path = require("path");
14 | const prependFile = require("prepend-file");
15 | const replace = require("replace-in-file");
16 | const system = require("system-commands");
17 | const pkg = require("../../package.json");
18 | const os = require("os");
19 | const fsPromises = require("fs").promises;
20 | const fs = require("fs-extra");
21 |
22 | const OUT_DIR = "dist";
23 |
24 | const WEB_MIDI_API_CLASSES = [
25 | "MIDIAccess",
26 | "MIDIConnectionEvent",
27 | "MIDIConnectionEventInit",
28 | "MIDIInput",
29 | "MIDIInputMap",
30 | "MIDIMessageEvent",
31 | "MIDIMessageEventInit",
32 | "MIDIOptions",
33 | "MIDIOutput",
34 | "MIDIOutputMap",
35 | "MIDIPort",
36 | "MIDIPortConnectionStatus",
37 | "MIDIPortDeviceState",
38 | "MIDIPortType"
39 | ];
40 |
41 | const HEADER = `// Type definitions for ${pkg.webmidi.name} ${pkg.version}\n` +
42 | `// Project: ${pkg.homepage}\n` +
43 | `// Definitions by: ${pkg.author.name} \n` +
44 | `// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped\n\n`;
45 |
46 | let targets = [];
47 |
48 | let type = "all";
49 | const argv = require("minimist")(process.argv.slice(2));
50 | if (["cjs", "esm"].includes(argv.t)) type = argv.t;
51 |
52 | const commit = argv.c === "true";
53 |
54 | if (type === "all" || type === "cjs")
55 | targets.push({source: "webmidi.cjs.js", name: "cjs", type: "CommonJS"});
56 |
57 | if (type === "all" || type === "esm")
58 | targets.push({source: "webmidi.esm.js", name: "esm", type: "ES2020"});
59 |
60 | async function execute() {
61 |
62 | // Temp dir
63 | const TMP_DIR_PATH = await fsPromises.mkdtemp(path.join(os.tmpdir(), "webmidi-ts-"));
64 |
65 | targets.forEach(async target => {
66 |
67 | // Make a copy of the source file so we can substitute some elements before parsing
68 | const TMP_FILE_PATH = path.join(TMP_DIR_PATH, target.source);
69 | await fs.copy(
70 | path.join(OUT_DIR, target.name, target.source),
71 | TMP_FILE_PATH,
72 | {overwrite: true}
73 | );
74 |
75 | // Add TypeScript WebMidi namespace before native Web MIDI API objects
76 | WEB_MIDI_API_CLASSES.forEach(element => {
77 |
78 | const options = {
79 | files: TMP_FILE_PATH,
80 | from: new RegExp("{" + element + "}", "g"),
81 | to: () => `{WebMidi.${element}}`
82 | };
83 |
84 | replace.sync(options);
85 |
86 | });
87 |
88 | // Replace callback type by simply "EventEmitterCallback" (this is needed because tsc does not
89 | // support tilde character in types. See below...
90 | replace.sync({
91 | files: TMP_FILE_PATH,
92 | from: new RegExp("{EventEmitter~callback}", "g"),
93 | to: () => "{EventEmitterCallback}"
94 | });
95 |
96 | // Generate declaration file
97 | const cmd = "npx -p typescript tsc " + TMP_FILE_PATH +
98 | " --declaration --allowJs --emitDeclarationOnly" +
99 | " --module " + target.type +
100 | " --lib ES2020,DOM --outDir " + path.join(OUT_DIR, target.name) ;
101 | await system(cmd);
102 |
103 | // Path to current .d.ts file
104 | const file = path.join(OUT_DIR, target.name, target.source.replace(".js", ".d.ts"));
105 |
106 | // Remove readonly flag
107 | const options = {
108 | files: [file],
109 | from: /readonly /g,
110 | to: ""
111 | };
112 | await replace(options);
113 |
114 |
115 |
116 |
117 |
118 | // // Inject correct definitions for Input class
119 | // await replace({
120 | // files: [file],
121 | // from: /export class Input extends EventEmitter \{/,
122 | // to: fs.readFileSync(
123 | // path.join(__dirname, "injections", "Input.txt"), {encoding: "utf8", flag: "r"}
124 | // )
125 | // });
126 | //
127 |
128 |
129 |
130 |
131 |
132 |
133 | // Prepend header for DefinitelyTyped
134 | await prependFile(file, HEADER);
135 | log("Saved " + target.type + " TypeScript declaration file to '" + file + "'");
136 |
137 | // // Inject EventEmitterCallback type declaration
138 | // fs.appendFileSync(
139 | // file,
140 | // `\n\n` +
141 | // `// This is automatically injected to fix a bug with the TypeScript compiler\n` +
142 | // `export type EventEmitterCallback = (...args: any[]) => void;\n`
143 | // // dans Tone.js, ils utilisent:
144 | // //`export type EventEmitterCallback = (...args: A) => void;`
145 | // );
146 |
147 | // Commit
148 | if (commit) {
149 | await git.add([file]);
150 | await git.commit("Generated by TypeScript compiler on " + moment().format());
151 | await git.push();
152 | }
153 |
154 | });
155 |
156 | }
157 |
158 | // Execute and catch errors if any (in red)
159 | execute().catch(error => console.error("\x1b[31m", "Error: " + error, "\x1b[0m"));
160 |
161 | function log(message) {
162 | console.info("\x1b[32m", message, "\x1b[0m");
163 | }
164 |
--------------------------------------------------------------------------------
/scripts/website/deploy.js:
--------------------------------------------------------------------------------
1 | // This script copies the files generated by Docusaurus in /website/build to the root of the
2 | // 'gh-pages' branch. This makes them available at djipco.github.io/webmidi
3 |
4 | // Importation
5 | const fs = require("fs-extra");
6 | const fsPromises = require("fs").promises;
7 | const git = require("simple-git")();
8 | const moment = require("moment");
9 | const os = require("os");
10 | const path = require("path");
11 | const rimraf = require("@alexbinary/rimraf");
12 |
13 | // Configuration
14 | const TARGET_BRANCH = "gh-pages";
15 | const SOURCE_DIR = path.join(process.cwd(), "website", "build");
16 |
17 | async function execute() {
18 |
19 | const TMP_DIR = await fsPromises.mkdtemp(path.join(os.tmpdir(), "webmidi-website-"));
20 |
21 | // Get list of files and directories inside the Docusaurus build directory
22 | let files = await fsPromises.readdir(SOURCE_DIR);
23 |
24 | // Get rid of .DS_Store and other unnecessary files
25 | files = files.filter(file => !file.startsWith("."));
26 |
27 | // Copy files to tmp directory
28 | for (const file of files) {
29 | const source = path.join(SOURCE_DIR, file);
30 | const target = path.join(TMP_DIR, file);
31 | await fs.copy(source, target, {overwrite: true});
32 | }
33 | log("Copied website files to temp dir: " + TMP_DIR);
34 |
35 | // Get current branch (so we can come back to it later)
36 | let results = await git.branch();
37 | const ORIGINAL_BRANCH = results.current;
38 |
39 | // Switch to target branch
40 | log(`Switching from '${ORIGINAL_BRANCH}' branch to '${TARGET_BRANCH}' branch`);
41 | await git.checkout(TARGET_BRANCH);
42 |
43 | // Copy content of tmp dir to final destination and commit
44 | for (const file of files) {
45 | const source = path.join(TMP_DIR, file);
46 | const target = path.join(process.cwd(), file);
47 | await fs.copy(source, target, {overwrite: true});
48 | await git.add([target]);
49 | await git.commit("Updated on: " + moment().format(), [target]);
50 | }
51 |
52 | await git.push();
53 | log(`Website files committed to '${TARGET_BRANCH}' branch and pushed to remote`);
54 |
55 | // Remove temp files
56 | rimraf(TMP_DIR);
57 | log(`Temp directory removed`);
58 |
59 | // Come back to original branch
60 | log(`Switching back to '${ORIGINAL_BRANCH}' branch`);
61 | await git.checkout(ORIGINAL_BRANCH);
62 |
63 | }
64 |
65 | function log(message) {
66 | console.info("\x1b[32m", message, "\x1b[0m");
67 | }
68 |
69 | // Execution
70 | execute().catch(error => console.error("\x1b[31m", "Error: " + error, "\x1b[0m"));
71 |
--------------------------------------------------------------------------------
/src/Forwarder.js:
--------------------------------------------------------------------------------
1 | import {Enumerations} from "./Enumerations.js";
2 | import {Output} from "./Output.js";
3 | import {WebMidi} from "./WebMidi.js";
4 |
5 | /**
6 | * The `Forwarder` class allows the forwarding of MIDI messages to predetermined outputs. When you
7 | * call its [`forward()`](#forward) method, it will send the specified [`Message`](Message) object
8 | * to all the outputs listed in its [`destinations`](#destinations) property.
9 | *
10 | * If specific channels or message types have been defined in the [`channels`](#channels) or
11 | * [`types`](#types) properties, only messages matching the channels/types will be forwarded.
12 | *
13 | * While it can be manually instantiated, you are more likely to come across a `Forwarder` object as
14 | * the return value of the [`Input.addForwarder()`](Input#addForwarder) method.
15 | *
16 | * @license Apache-2.0
17 | * @since 3.0.0
18 | */
19 | export class Forwarder {
20 |
21 | /**
22 | * Creates a `Forwarder` object.
23 | *
24 | * @param {Output|Output[]} [destinations=\[\]] An [`Output`](Output) object, or an array of such
25 | * objects, to forward the message to.
26 | *
27 | * @param {object} [options={}]
28 | * @param {string|string[]} [options.types=(all messages)] A MIDI message type or an array of such
29 | * types (`"noteon"`, `"controlchange"`, etc.), that the specified message must match in order to
30 | * be forwarded. If this option is not specified, all types of messages will be forwarded. Valid
31 | * messages are the ones found in either
32 | * [`SYSTEM_MESSAGES`](Enumerations#SYSTEM_MESSAGES)
33 | * or [`CHANNEL_MESSAGES`](Enumerations#CHANNEL_MESSAGES).
34 | * @param {number|number[]} [options.channels=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]]
35 | * A MIDI channel number or an array of channel numbers that the message must match in order to be
36 | * forwarded. By default all MIDI channels are included (`1` to `16`).
37 | */
38 | constructor(destinations = [], options = {}) {
39 |
40 | /**
41 | * An array of [`Output`](Output) objects to forward the message to.
42 | * @type {Output[]}
43 | */
44 | this.destinations = [];
45 |
46 | /**
47 | * An array of message types (`"noteon"`, `"controlchange"`, etc.) that must be matched in order
48 | * for messages to be forwarded. By default, this array includes all
49 | * [`Enumerations.SYSTEM_MESSAGES`](Enumerations#SYSTEM_MESSAGES) and
50 | * [`Enumerations.CHANNEL_MESSAGES`](Enumerations#CHANNEL_MESSAGES).
51 | * @type {string[]}
52 | */
53 | this.types = [
54 | ...Object.keys(Enumerations.SYSTEM_MESSAGES),
55 | ...Object.keys(Enumerations.CHANNEL_MESSAGES)
56 | ];
57 |
58 | /**
59 | * An array of MIDI channel numbers that the message must match in order to be forwarded. By
60 | * default, this array includes all MIDI channels (`1` to `16`).
61 | * @type {number[]}
62 | */
63 | this.channels = Enumerations.MIDI_CHANNEL_NUMBERS;
64 |
65 | /**
66 | * Indicates whether message forwarding is currently suspended or not in this forwarder.
67 | * @type {boolean}
68 | */
69 | this.suspended = false;
70 |
71 | // Make sure parameters are arrays
72 | if (!Array.isArray(destinations)) destinations = [destinations];
73 | if (options.types && !Array.isArray(options.types)) options.types = [options.types];
74 | if (options.channels && !Array.isArray(options.channels)) options.channels = [options.channels];
75 |
76 | if (WebMidi.validation) {
77 |
78 | // Validate destinations
79 | destinations.forEach(destination => {
80 | if ( !(destination instanceof Output) ) {
81 | throw new TypeError("Destinations must be of type 'Output'.");
82 | }
83 | });
84 |
85 | // Validate types
86 | if (options.types !== undefined) {
87 |
88 | options.types.forEach(type => {
89 | if (
90 | ! Enumerations.SYSTEM_MESSAGES.hasOwnProperty(type) &&
91 | ! Enumerations.CHANNEL_MESSAGES.hasOwnProperty(type)
92 | ) {
93 | throw new TypeError("Type must be a valid message type.");
94 | }
95 | });
96 |
97 | }
98 |
99 | // Validate channels
100 | if (options.channels !== undefined) {
101 |
102 | options.channels.forEach(channel => {
103 | if (! Enumerations.MIDI_CHANNEL_NUMBERS.includes(channel) ) {
104 | throw new TypeError("MIDI channel must be between 1 and 16.");
105 | }
106 | });
107 |
108 | }
109 |
110 | }
111 |
112 | this.destinations = destinations;
113 | if (options.types) this.types = options.types;
114 | if (options.channels) this.channels = options.channels;
115 |
116 | }
117 |
118 | /**
119 | * Sends the specified message to the forwarder's destination(s) if it matches the specified
120 | * type(s) and channel(s).
121 | *
122 | * @param {Message} message The [`Message`](Message) object to forward.
123 | */
124 | forward(message) {
125 |
126 | // Abort if forwarding is currently suspended
127 | if (this.suspended) return;
128 |
129 | // Abort if this message type should not be forwarded
130 | if (!this.types.includes(message.type)) return;
131 |
132 | // Abort if this channel should not be forwarded
133 | if (message.channel && !this.channels.includes(message.channel)) return;
134 |
135 | // Forward
136 | this.destinations.forEach(destination => {
137 | if (WebMidi.validation && !(destination instanceof Output)) return;
138 | destination.send(message);
139 | });
140 |
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/test/Enumerations.test.js:
--------------------------------------------------------------------------------
1 | const expect = require("chai").expect;
2 | const {Enumerations} = require("../dist/cjs/webmidi.cjs.js");
3 |
4 | // VERIFIED
5 | describe("Enumerations Object", function() {
6 |
7 | describe("get CHANNEL_MESSAGES()", function() {
8 | it("should return an object with valid properties", function() {
9 | expect(Enumerations.CHANNEL_MESSAGES.pitchbend).to.equal(0xE);
10 | });
11 | });
12 |
13 | describe("get MIDI_REGISTERED_PARAMETER()", function() {
14 | it("should return an object with valid properties", function() {
15 | expect(Enumerations.REGISTERED_PARAMETERS.rollangle[0]).to.equal(0x3D);
16 | expect(Enumerations.REGISTERED_PARAMETERS.rollangle[1]).to.equal(0x08);
17 | });
18 | });
19 |
20 | describe("get CONTROL_CHANGE_MESSAGES()", function() {
21 | it("should return an an array with valid properties", function() {
22 | expect(Enumerations.CONTROL_CHANGE_MESSAGES[73].name).to.equal("attacktime");
23 | });
24 | });
25 |
26 | // Legacy (remove in v4)
27 | describe("get MIDI_CONTROL_CHANGE_MESSAGES()", function() {
28 | it("should return an object with valid properties", function() {
29 | expect(Enumerations.MIDI_CONTROL_CHANGE_MESSAGES.registeredparameterfine).to.equal(101);
30 | });
31 | });
32 |
33 | });
34 |
--------------------------------------------------------------------------------
/test/Message.test.js:
--------------------------------------------------------------------------------
1 | const expect = require("chai").expect;
2 | const {Message, Enumerations} = require("../dist/cjs/webmidi.cjs.js");
3 |
4 | describe("Message Object", function() {
5 |
6 | describe("constructor()", function() {
7 |
8 | it("should correctly set the type of message for system messages", function() {
9 |
10 | // Arrange
11 | let data = new Uint8Array(3);
12 |
13 | const items = [
14 | {type: "sysex", status: 240},
15 | {type: "timecode", status: 241},
16 | {type: "songposition", status: 242},
17 | {type: "songselect", status: 243},
18 | {type: "tunerequest", status: 246},
19 | {type: "sysexend", status: 247},
20 | {type: "clock", status: 248},
21 | {type: "start", status: 250},
22 | {type: "continue", status: 251},
23 | {type: "stop", status: 252},
24 | {type: "activesensing", status: 254},
25 | {type: "reset", status: 255}
26 | ];
27 |
28 | // Act
29 | items.forEach(item => {
30 | data[0] = item.status;
31 | const message = new Message(data);
32 | expect(message.isSystemMessage).to.be.true;
33 | expect(message.isChannelMessage).to.be.false;
34 | expect(message.type).to.equal(item.type);
35 | });
36 |
37 | });
38 |
39 | it("should correctly set properties for channel messages", function() {
40 |
41 | // Arrange
42 | const items = [
43 | {data: [0x8, 0, 127], type: "noteoff"},
44 | {data: [0x9, 32, 96], type: "noteon"},
45 | {data: [0xA, 64, 64], type: "keyaftertouch"},
46 | {data: [0xB, 96, 32], type: "controlchange"},
47 | {data: [0xC, 0], type: "programchange"},
48 | {data: [0xD, 64], type: "channelaftertouch"},
49 | {data: [0xE, 32, 64], type: "pitchbend"}
50 | ];
51 | const channel = 10;
52 |
53 | // Act
54 | items.forEach(item => {
55 | item.data[0] = (item.data[0] << 4) + channel - 1;
56 | const message = new Message(item.data);
57 | expect(message.isSystemMessage).to.be.false;
58 | expect(message.isChannelMessage).to.be.true;
59 | expect(message.type).to.equal(item.type);
60 | expect(message.rawData).to.equal(item.data);
61 | });
62 |
63 | });
64 |
65 | it("should correctly set properties for sysex with basic manufacturer code", function() {
66 |
67 | // Arrange
68 | let data = new Uint8Array(6);
69 | data[0] = Enumerations.MIDI_SYSTEM_MESSAGES.sysex; // sysex
70 | data[1] = 0x42; // Korg
71 | data[2] = 1; // Some data
72 | data[3] = 2; // Some data
73 | data[4] = 3; // Some data
74 | data[5] = Enumerations.MIDI_SYSTEM_MESSAGES.sysexend; // sysex end
75 |
76 | // Act
77 | const message = new Message(data);
78 |
79 | // Assert
80 | expect(message.manufacturerId).to.have.ordered.members([data[1]]);
81 | expect(message.dataBytes).to.have.ordered.members([data[2], data[3], data[4]]);
82 |
83 | });
84 |
85 | it("should correctly set properties for sysex with extended manufacturer code", function() {
86 |
87 | // Arrange
88 | let data = new Uint8Array(8);
89 | data[0] = Enumerations.MIDI_SYSTEM_MESSAGES.sysex; // sysex
90 | data[1] = 0; // MOTU (byte 1)
91 | data[2] = 0; // MOTU (byte 2)
92 | data[3] = 0x3B; // MOTU (byte 3)
93 | data[4] = 1; // Some data
94 | data[5] = 2; // Some data
95 | data[6] = 3; // Some data
96 | data[7] = Enumerations.MIDI_SYSTEM_MESSAGES.sysexend; // sysex end
97 |
98 | // Act
99 | const message = new Message(data);
100 |
101 | // Assert
102 | expect(message.manufacturerId).to.have.ordered.members([data[1], data[2], data[3]]);
103 | expect(message.dataBytes).to.have.ordered.members([data[4], data[5], data[6]]);
104 |
105 | });
106 |
107 | });
108 |
109 | });
110 |
111 |
112 |
--------------------------------------------------------------------------------
/test/support/Utils.cjs.js:
--------------------------------------------------------------------------------
1 | const UtilsCjs = {
2 |
3 | isNative: function(fn) {
4 | return (/\{\s*\[native code\]\s*\}/).test("" + fn);
5 | }
6 |
7 | };
8 |
9 | module.exports = UtilsCjs;
10 |
--------------------------------------------------------------------------------
/test/support/Utils.iife.js:
--------------------------------------------------------------------------------
1 | function isNative(fn) {
2 | return (/\{\s*\[native code\]\s*\}/).test("" + fn);
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
4 |
5 | ## Installation
6 |
7 | ```console
8 | yarn install
9 | ```
10 |
11 | ## Local Development
12 |
13 | ```console
14 | yarn start
15 | ```
16 |
17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ## Build
20 |
21 | ```console
22 | yarn build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ## Deployment
28 |
29 | ```console
30 | GIT_USER= USE_SSH=true yarn deploy
31 | ```
32 |
33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
34 |
--------------------------------------------------------------------------------
/website/api/classes/Forwarder.md:
--------------------------------------------------------------------------------
1 |
2 | # Forwarder
3 |
4 | The `Forwarder` class allows the forwarding of MIDI messages to predetermined outputs. When you
5 | call its [`forward()`](#forward) method, it will send the specified [`Message`](Message) object
6 | to all the outputs listed in its [`destinations`](#destinations) property.
7 |
8 | If specific channels or message types have been defined in the [`channels`](#channels) or
9 | [`types`](#types) properties, only messages matching the channels/types will be forwarded.
10 |
11 | While it can be manually instantiated, you are more likely to come across a `Forwarder` object as
12 | the return value of the [`Input.addForwarder()`](Input#addForwarder) method.
13 |
14 | **Since**: 3.0.0
15 |
16 |
17 |
18 | ### `Constructor`
19 |
20 | Creates a `Forwarder` object.
21 |
22 |
23 | **Parameters**
24 |
25 | > `new Forwarder([destinations], [options])`
26 |
27 |
28 |
29 | | Parameter | Type | Default | Description |
30 | | ------------ | ------------ | ------------ | ------------ |
31 | |[**`destinations`**] | Output Array.<Output> |\[\]|An [`Output`](Output) object, or an array of such objects, to forward the message to.|
32 | |[**`options`**] | object |{}||
33 | |[**`options.types`**] | string Array.<string> |(all messages)|A MIDI message type or an array of such types (`"noteon"`, `"controlchange"`, etc.), that the specified message must match in order to be forwarded. If this option is not specified, all types of messages will be forwarded. Valid messages are the ones found in either [`SYSTEM_MESSAGES`](Enumerations#SYSTEM_MESSAGES) or [`CHANNEL_MESSAGES`](Enumerations#CHANNEL_MESSAGES).|
34 | |[**`options.channels`**] | number Array.<number> |[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]|A MIDI channel number or an array of channel numbers that the message must match in order to be forwarded. By default all MIDI channels are included (`1` to `16`).|
35 |
36 |
37 |
38 |
39 |
40 | ***
41 |
42 | ## Properties
43 |
44 | ### `.channels` {#channels}
45 | **Type**: Array.<number>
46 |
47 |
48 | An array of MIDI channel numbers that the message must match in order to be forwarded. By
49 | default, this array includes all MIDI channels (`1` to `16`).
50 |
51 |
52 | ### `.destinations` {#destinations}
53 | **Type**: Array.<Output>
54 |
55 |
56 | An array of [`Output`](Output) objects to forward the message to.
57 |
58 |
59 | ### `.suspended` {#suspended}
60 | **Type**: boolean
61 |
62 |
63 | Indicates whether message forwarding is currently suspended or not in this forwarder.
64 |
65 |
66 | ### `.types` {#types}
67 | **Type**: Array.<string>
68 |
69 |
70 | An array of message types (`"noteon"`, `"controlchange"`, etc.) that must be matched in order
71 | for messages to be forwarded. By default, this array includes all
72 | [`Enumerations.SYSTEM_MESSAGES`](Enumerations#SYSTEM_MESSAGES) and
73 | [`Enumerations.CHANNEL_MESSAGES`](Enumerations#CHANNEL_MESSAGES).
74 |
75 |
76 |
77 | ***
78 |
79 | ## Methods
80 |
81 |
82 | ### `.forward(...)` {#forward}
83 |
84 |
85 | Sends the specified message to the forwarder's destination(s) if it matches the specified
86 | type(s) and channel(s).
87 |
88 |
89 | **Parameters**
90 |
91 | > Signature: `forward(message)`
92 |
93 |
94 |
95 | | Parameter | Type(s) | Default | Description |
96 | | ------------ | ------------ | ------------ | ------------ |
97 | |**`message`** | Message ||The [`Message`](Message) object to forward.|
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/website/api/classes/Listener.md:
--------------------------------------------------------------------------------
1 |
2 | # Listener
3 |
4 | The `Listener` class represents a single event listener object. Such objects keep all relevant
5 | contextual information such as the event being listened to, the object the listener was attached
6 | to, the callback function and so on.
7 |
8 |
9 |
10 |
11 | ### `Constructor`
12 |
13 | Creates a new `Listener` object
14 |
15 |
16 | **Parameters**
17 |
18 | > `new Listener(event, target, callback, [options])`
19 |
20 |
21 |
22 | | Parameter | Type | Default | Description |
23 | | ------------ | ------------ | ------------ | ------------ |
24 | |**`event`** | string Symbol ||The event being listened to|
25 | |**`target`** | EventEmitter ||The [`EventEmitter`](EventEmitter) object that the listener is attached to.|
26 | |**`callback`** | EventEmitter~callback ||The function to call when the listener is triggered|
27 | |[**`options`**] | Object |{}||
28 | |[**`options.context`**] | Object |target|The context to invoke the listener in (a.k.a. the value of `this` inside the callback function).|
29 | |[**`options.remaining`**] | number |Infinity|The remaining number of times after which the callback should automatically be removed.|
30 | |[**`options.arguments`**] | array ||An array of arguments that will be passed separately to the callback function upon execution. The array is stored in the [`arguments`](#arguments) property and can be retrieved or modified as desired.|
31 |
32 |
33 |
34 |
35 | **Throws**:
36 | * `TypeError` : The `event` parameter must be a string or
37 | [`EventEmitter.ANY_EVENT`](EventEmitter#ANY_EVENT).
38 | * `ReferenceError` : The `target` parameter is mandatory.
39 | * `TypeError` : The `callback` must be a function.
40 |
41 | ***
42 |
43 | ## Properties
44 |
45 | ### `.arguments` {#arguments}
46 | **Type**: array
47 |
48 |
49 | An array of arguments to pass to the callback function upon execution.
50 |
51 |
52 | ### `.callback` {#callback}
53 | **Type**: function
54 |
55 |
56 | The callback function to execute.
57 |
58 |
59 | ### `.context` {#context}
60 | **Type**: Object
61 |
62 |
63 | The context to execute the callback function in (a.k.a. the value of `this` inside the
64 | callback function)
65 |
66 |
67 | ### `.count` {#count}
68 | **Type**: number
69 |
70 |
71 | The number of times the listener function was executed.
72 |
73 |
74 | ### `.event` {#event}
75 | **Type**: string
76 |
77 |
78 | The event name.
79 |
80 |
81 | ### `.remaining` {#remaining}
82 | **Type**: number
83 |
84 |
85 | The remaining number of times after which the callback should automatically be removed.
86 |
87 |
88 | ### `.suspended` {#suspended}
89 | **Type**: boolean
90 |
91 |
92 | Whether this listener is currently suspended or not.
93 |
94 |
95 | ### `.target` {#target}
96 | **Type**: EventEmitter
97 |
98 |
99 | The object that the event is attached to (or that emitted the event).
100 |
101 |
102 |
103 | ***
104 |
105 | ## Methods
106 |
107 |
108 | ### `.remove()` {#remove}
109 |
110 |
111 | Removes the listener from its target.
112 |
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/website/api/classes/Message.md:
--------------------------------------------------------------------------------
1 |
2 | # Message
3 |
4 | The `Message` class represents a single MIDI message. It has several properties that make it
5 | easy to make sense of the binary data it contains.
6 |
7 | **Since**: 3.0.0
8 |
9 |
10 |
11 | ### `Constructor`
12 |
13 | Creates a new `Message` object from raw MIDI data.
14 |
15 |
16 | **Parameters**
17 |
18 | > `new Message(data)`
19 |
20 |
21 |
22 | | Parameter | Type | Default | Description |
23 | | ------------ | ------------ | ------------ | ------------ |
24 | |**`data`** | Uint8Array ||The raw data of the MIDI message as a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) of integers between `0` and `255`.|
25 |
26 |
27 |
28 |
29 |
30 | ***
31 |
32 | ## Properties
33 |
34 | ### `.channel` {#channel}
35 | **Type**: number
36 | **Attributes**: read-only
37 |
38 |
39 | The MIDI channel number (`1` - `16`) that the message is targeting. This is only for
40 | channel-specific messages. For system messages, this will be left `undefined`.
41 |
42 |
43 | ### `.command` {#command}
44 | **Type**: number
45 | **Attributes**: read-only
46 |
47 |
48 | An integer identifying the MIDI command. For channel-specific messages, the value is 4-bit
49 | and will be between `8` and `14`. For system messages, the value will be between `240` and
50 | `255`.
51 |
52 |
53 | ### `.data` {#data}
54 | **Type**: Array.<number>
55 | **Attributes**: read-only
56 |
57 |
58 | An array containing all the bytes of the MIDI message. Each byte is an integer between `0`
59 | and `255`.
60 |
61 |
62 | ### `.dataBytes` {#dataBytes}
63 | **Type**: Array.<number>
64 | **Attributes**: read-only
65 |
66 |
67 | An array of the the data byte(s) of the MIDI message (as opposed to the status byte). When
68 | the message is a system exclusive message (sysex), `dataBytes` explicitly excludes the
69 | manufacturer ID and the sysex end byte so only the actual data is included.
70 |
71 |
72 | ### `.isChannelMessage` {#isChannelMessage}
73 | **Type**: boolean
74 | **Attributes**: read-only
75 |
76 |
77 | A boolean indicating whether the MIDI message is a channel-specific message.
78 |
79 |
80 | ### `.isSystemMessage` {#isSystemMessage}
81 | **Type**: boolean
82 | **Attributes**: read-only
83 |
84 |
85 | A boolean indicating whether the MIDI message is a system message (not specific to a
86 | channel).
87 |
88 |
89 | ### `.manufacturerId` {#manufacturerId}
90 | **Type**: Array.<number>
91 | **Attributes**: read-only
92 |
93 |
94 | When the message is a system exclusive message (sysex), this property contains an array with
95 | either 1 or 3 entries that identify the manufacturer targeted by the message.
96 |
97 | To know how to translate these entries into manufacturer names, check out the official list:
98 | https://www.midi.org/specifications-old/item/manufacturer-id-numbers
99 |
100 |
101 | ### `.rawData` {#rawData}
102 | **Type**: Uint8Array
103 | **Attributes**: read-only
104 |
105 |
106 | A
107 | [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
108 | containing the bytes of the MIDI message. Each byte is an integer between `0` and `255`.
109 |
110 |
111 | ### `.rawDataBytes` {#rawDataBytes}
112 | **Type**: Uint8Array
113 | **Attributes**: read-only
114 |
115 |
116 | A
117 | [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
118 | of the data byte(s) of the MIDI message. When the message is a system exclusive message
119 | (sysex), `rawDataBytes` explicitly excludes the manufacturer ID and the sysex end byte so
120 | only the actual data is included.
121 |
122 |
123 | ### `.statusByte` {#statusByte}
124 | **Type**: number
125 | **Attributes**: read-only
126 |
127 |
128 | The MIDI status byte of the message as an integer between `0` and `255`.
129 |
130 |
131 | ### `.type` {#type}
132 | **Type**: string
133 | **Attributes**: read-only
134 |
135 |
136 | The type of message as a string (`"noteon"`, `"controlchange"`, `"sysex"`, etc.)
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/website/api/classes/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Classes & Objects",
3 | "position": 2,
4 | "collapsed": false
5 | }
6 |
--------------------------------------------------------------------------------
/website/api/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | title: API Documentation
4 | slug: /
5 | ---
6 |
7 | # API Documentation
8 |
9 | ## Core Classes
10 |
11 | These classes are the ones developers are most likely to be dealing with while working on their MIDI
12 | projects. Note that all these classes are pre-instantiated within WEBMIDI.js.
13 |
14 | * [**WebMidi**](./classes/WebMidi.md)
15 | * [**Input**](./classes/Input.md)
16 | * [**InputChannel**](./classes/InputChannel.md)
17 | * [**Output**](./classes/Output.md)
18 | * [**OutputChannel**](./classes/OutputChannel.md)
19 | * [**Message**](./classes/Message.md)
20 |
21 | The exception are the [`Note`](./classes/Note.md) class which you can instantiate when you need
22 | to store a musical note and the [`Forwarder`](./classes/Forwarder.md) class used to forward
23 | messages from an input to an output:
24 |
25 | * [**Note**](./classes/Note.md)
26 | * [**Forwarder**](./classes/Forwarder.md)
27 |
28 | ## Support Classes
29 |
30 | These classes are mostly for internal use, but you might find them useful in some contexts. The
31 | [`Enumerations`](./classes/Enumerations.md) class contains static enums of MIDI messages,
32 | registered parameters, etc. The [`Utilities`](./classes/Utilities.md) class contains various
33 | static methods.
34 |
35 | * [**Enumerations**](./classes/Enumerations.md)
36 | * [**Utilities**](./classes/Utilities.md)
37 |
38 | ## DjipEvents Classes
39 |
40 | The `EventEmitter` and `Listener` classes from the
41 | [DjipEvents](https://github.com/djipco/djipevents) module are extended by various WEBMIDI.js
42 | classes. So, in the interest of completeness, we include their full documentation here and
43 | cross-reference it with the core classes
44 |
45 | * [**EventEmitter**](./classes/EventEmitter.md)
46 | * [**Listener**](./classes/Listener.md)
47 |
--------------------------------------------------------------------------------
/website/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve("@docusaurus/core/lib/babel/preset")],
3 | };
4 |
--------------------------------------------------------------------------------
/website/blog/2021-12-01/version-3-has-been-released.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: WEBMIDI.js v3 is available now!
3 | description: Version 3 of WEBMIDI.js, the library that lets you interact with your MIDI instruments and devices, is now available. It features Node.js and TypeScript support, various new objects (Message, Note, etc.) and a completely rewritten engine.
4 | authors:
5 | - name: Jean-Philippe Côté
6 | title: Creator of WEBMIDI.js
7 | url: /about
8 | image_url: /img/blog/jean-philippe_cote.jpg
9 | hide_table_of_contents: false
10 | keywords: [web midi api, music, instrument, midi, javascript]
11 | image: /img/blog/2021-12-01/webmidijs-is-out.png
12 | ---
13 |
14 | After a lot of work and testing, I am happy to announce today that version 3 of the go-to MIDI
15 | library for JavaScript has been released! You can [try it out](https://webmidijs.org/docs) right
16 | now!
17 |
18 |
19 |
20 | 
21 |
22 | ### About WEBMIDI.js
23 |
24 | [**WEBMIDI.js**](https://webmidijs.org) exists to make it easier for developers to use the
25 | [Web MIDI API](https://webaudio.github.io/web-midi-api/). The Web MIDI API is a really exciting
26 | addition to the web platform allowing a web page to interact directly with MIDI musical instruments
27 | and devices.
28 |
29 | While great, many developers will find the API to be too low-level for their needs. Having to
30 | perform binary arithmetic or needing to constantly refer to the 300-page MIDI spec is no fun (trust
31 | me on this!). So, the goal for [**WEBMIDI.js**](https://webmidijs.org) is to get developers and
32 | musicians started with their web-based MIDI projects as efficiently as possible.
33 |
34 | As of today, [**WEBMIDI.js**](https://webmidijs.org) generates over **744K hits a month on
35 | [jsDelivr](https://www.jsdelivr.com/package/npm/webmidi)**. It is **downloaded over 4.4K times a
36 | month on [NPM](https://www.npmjs.com/package/webmidi)** and has been **starred by over
37 | [1000 developers](https://github.com/djipco/webmidi/stargazers)** on GitHub. Not too bad for a niche
38 | library that grew out of a personal passion project. 😀
39 |
40 | ### About the New Version 3
41 |
42 | Version 3 has been rewritten from scratch to make it both future-proof and backwards-compatible. It
43 | uses a modern development paradigm and now has its own dedicated website at
44 | [**webmidijs.org**](https://webmidijs.org). The library offers numerous new features such as:
45 |
46 | * Long-awaited **support for Node.js** (thanks to the [jzz](https://www.npmjs.com/package/jzz)
47 | module by Jazz-Soft). The exact same code can be used in supported browsers and in Node.js.
48 |
49 | * Distribution in **3 flavours**: **ESM** (ECMAScript module for modern browsers), **CJS** (CommonJS
50 | module for Node.js) and **IIFE** (Immediately Invoked Function Expression for legacy browsers and
51 | _ad hoc_ usage).
52 |
53 | * **TypeScript Support**. Every new release includes a TypeScript definition file for CJS and ESM in
54 | the `dist` directory.
55 |
56 | * **New `InputChannel` and `OutputChannel`** objects. You can now work with a single MIDI channel if
57 | that's appropriate for your needs.
58 |
59 | * **New `Note` object**. Makes it easier to work with notes and pass them around from one method to
60 | the next.
61 |
62 | * **New `Message` object** that allows easier routing of MIDI messages, including the ability to
63 | automatically **forward inbound MIDI messages** to one, or more, outputs (much like the good ol'
64 | physical THRU port).
65 |
66 | * Improved support for **system exclusive** (sysex) messages.
67 |
68 | * **Support for promises** while preserving legacy callback support.
69 |
70 | * Improved **support for RPN/NRPN messages**.
71 |
72 | * Addition of **hundreds of unit tests** to make sure the library remains stable at all times.
73 |
74 | * and lots more...
75 |
76 | ### Try it out!
77 |
78 | The [documentation section](https://webmidijs.org/docs) of the new website has all the information
79 | to get you started. If you need help, you can exchange with fellow users and myself using the
80 | [GitHub Discussions](https://github.com/djipco/webmidi/discussions) platform.
81 |
82 | If you use the library and find it useful, please think about
83 | [sponsoring](https://github.com/sponsors/djipco) 💜 the project.
84 |
85 | Cheers!
86 |
87 | Jean-Philippe
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/website/blog/2021-12-01/webmidi.js-is-available-now.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/blog/2021-12-01/webmidi.js-is-available-now.png
--------------------------------------------------------------------------------
/website/docs/archives/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Previous Versions",
3 | "position": 40
4 | }
5 |
--------------------------------------------------------------------------------
/website/docs/archives/v1.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | slug: /archives/v1
4 | sidebar_label: Version 1.0.0-beta.15
5 | ---
6 |
7 | # Documentation for v1.0.0-beta.15
8 |
9 | :::caution
10 |
11 | There is no documentation per se for version 1.0.0-beta.15, However, you can still consult an
12 | archived copy of the full
13 | [API Reference](https://djipco.github.io/webmidi/archives/api/v1/classes/WebMidi.html).
14 |
15 | :::
16 |
--------------------------------------------------------------------------------
/website/docs/getting-started/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Getting Started",
3 | "position": 10
4 | }
5 |
--------------------------------------------------------------------------------
/website/docs/getting-started/installation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Installation
6 |
7 | ## Distribution Flavours
8 |
9 | To cater to various needs, WEBMIDI.js is distributed in 3 different flavours:
10 |
11 | * **Immediately Invoked Function Expression** (IIFE): This version adds its objects directly to the
12 | global namespace. This is the legacy approach which is often easier for beginners.
13 |
14 | * **ES6 Module** (ESM): This is the modern approach which allows you to `import` the objects as
15 | needed (works in newer versions of browsers and Node.js).
16 |
17 | * **CommonJS Module** (CJS): this is the flavour traditionnally used by Node.js and often with
18 | bundling tools such as WebPack.
19 |
20 | All 3 flavours come in full and minified versions with sourcemap.
21 |
22 |
23 | ## Retrieving the Library
24 |
25 | Depending on your needs and environment, you can retrieve and install **WEBMIDI.js** in a variety of
26 | different ways. Let's look at all of them.
27 |
28 | ## Linking From CDN
29 |
30 | The fastest way to get started is to link the library directly from the
31 | [jsDelivr](https://www.jsdelivr.com/package/npm/webmidi) CDN (Content Delivery Network). Just add a
32 | `
36 | ```
37 |
38 | You can retrieve different versions and flavours of the library by modifying the URL. For example,
39 | to grab a different flavour replace `/dist/iife/webmidi.iife.js` by one of these:
40 |
41 | * `/dist/cjs/webmidi.cjs.js`
42 | * `/dist/cjs/webmidi.cjs.min.js`
43 | * `/dist/esm/webmidi.esm.js`
44 | * `/dist/esm/webmidi.esm.min.js`
45 | * `/dist/iife/webmidi.iife.js`
46 | * `/dist/iife/webmidi.iife.min.js`
47 |
48 | If you want more control over versions and flavours, check out the
49 | [jsDelivr examples](https://www.jsdelivr.com/features).
50 |
51 | ## Installing Manually
52 |
53 | Obviously, you can also install the library the old-fashioned way by manually
54 | [downloading it](https://cdn.jsdelivr.net/npm/webmidi@latest/dist/iife/webmidi.iife.min.js) and
55 | placing it somewhere in your project. Link to it from your HTML page using a `
79 | ```
80 | * ### CommonJS
81 |
82 | Using **CommonJS** is the traditional approach for Node.js.
83 |
84 | ```javascript
85 | const {WebMidi} = require("webmidi");
86 | ```
87 | * ### ES Module
88 |
89 | This is the modern approach using the
90 | [ECMAScript module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) format.
91 | You can use it in browsers (see [compatibility](https://caniuse.com/es6-module-dynamic-import))
92 | and in Node.js v12+. **Going forward, this is the favoured approach.**
93 |
94 | **Browsers:**
95 |
96 | ```javascript
97 | import {WebMidi} from "./node_modules/webmidi/dist/esm/webmidi.esm.min.js";
98 | ```
99 |
100 | **Node.js:**
101 |
102 | ```javascript
103 | import {WebMidi} from "webmidi";
104 | ```
105 |
106 |
107 |
108 | :::caution
109 |
110 | ## Insecure Origins
111 |
112 | Starting with version 77,
113 | [Chrome deprecated Web MIDI usage on insecure origins](https://www.chromestatus.com/feature/5138066234671104).
114 | This means that, going forward, any page using the library will need to be hosted on a secure
115 | origin:
116 |
117 | * `https://`
118 | * `localhost:`
119 | * `file:///`
120 |
121 | Also, the user will need to explicitely authorize usage via a prompt (no matter if system exclusive
122 | messages are used or not).
123 |
124 | :::
125 |
--------------------------------------------------------------------------------
/website/docs/getting-started/supported-environments.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | slug: /getting-started
4 | ---
5 |
6 | # Supported Environments
7 |
8 | Starting with version 3, the library works in both the browser and Node.js. Let's quickly look at
9 | the specificities of both these environments.
10 |
11 | ## Browser Support
12 |
13 | The library works in all browsers that natively [support](https://caniuse.com/midi) the
14 | [Web MIDI API](https://webaudio.github.io/web-midi-api/). Currently, the following major browsers
15 | have native support:
16 |
17 | * Edge v79+
18 | * Chrome 43+
19 | * Opera 30+
20 | * Firefox 108+
21 |
22 | It is also possible to use this library in other browsers if you install
23 | [Jazz-Plugin](https://jazz-soft.net/download/Jazz-Plugin/) v1.4+. This combination provides
24 | support for the following additional web browsers:
25 |
26 | * Safari
27 | * Internet Explorer
28 |
29 | Note that, in 2020, [Apple has announced](https://webkit.org/tracking-prevention/) that they would
30 | not natively support the Web MIDI API (and a host of other APIs) in Safari because of fingerprinting
31 | concerns.
32 |
33 | ## Node.js Support
34 |
35 | Version 3.0 of WEBMIDI.js introduced full Node.js support. Nothing special needs to be done, it
36 | should just work in the following environments (with Node.js 8.5+):
37 |
38 | * GNU/Linux
39 | * macOS
40 | * Windows
41 | * Raspberry Pi
42 |
43 | Support for the Node.js environment has been made possible in large part by the good folks of
44 | [Jazz-Soft](https://jazz-soft.net/) via their [JZZ](https://www.npmjs.com/package/jzz) module.
45 |
46 | ## TypeScript Support
47 |
48 | Starting with version 3, TypeScript is officially supported. You will find the TypeScript definition
49 | file in these locations in side tje library's folder:
50 |
51 | * `/dist/cjs/webmidi.cjs.d.ts` (Node.js module)
52 | * `/dist/esm/webmidi.esm.d.ts` (ECMAScript module)
53 |
--------------------------------------------------------------------------------
/website/docs/going-further/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Going Further",
3 | "position": 20
4 | }
5 |
--------------------------------------------------------------------------------
/website/docs/going-further/electron.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 5
3 | title: Electron
4 | ---
5 |
6 | # Electron
7 |
8 | WEBMIDI.js works fine inside [Electron](https://www.electronjs.org/) but you must make sure to
9 | properly handle the permission request and permission check handlers from within the main process:
10 |
11 | ```javascript
12 | mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, callback, details) => {
13 | if (permission === 'midi' || permission === 'midiSysex') {
14 | callback(true);
15 | } else {
16 | callback(false);
17 | }
18 | })
19 |
20 | mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin) => {
21 | if (permission === 'midi' || permission === 'midiSysex') {
22 | return true;
23 | }
24 | return false;
25 | });
26 | ```
27 |
--------------------------------------------------------------------------------
/website/docs/going-further/forwarding.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | title: Forwarding
4 | ---
5 |
6 | # Forwarding Messages
7 |
8 | Starting with version 3, it is now possible to forward messages from an
9 | [`Input`](/api/classes/Input) to an [`Output`](/api/classes/Output). This is done by using the
10 | `forward` method of the [`Input`](/api/classes/Input) object.
11 |
--------------------------------------------------------------------------------
/website/docs/going-further/middle-c.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Middle C & Octave Offset
6 |
7 | ## Default Value for Middle C
8 |
9 | The general MIDI 1.0 specification does not explicitly define a pitch for middle C but it does
10 | consider middle C to be note number 60. The **MIDI Tuning Standard** states that note number 69
11 | should be tuned at 440Hz by default, which would make middle C (60) to be C4. However, different
12 | manufacturers have assigned middle C to various octaves/pitches (usually C3, C4 or C5).
13 |
14 | In accordance with the **MIDI Tuning Standard** and the
15 | [**scientific pitch notation**](https://en.wikipedia.org/wiki/Scientific_pitch_notation), WEBMIDI.js
16 | considers middle C (261.626 Hz) to be C4 by default.
17 |
18 | ## Offsetting Middle C
19 |
20 | You can offset the reported note name and octave by using the `octaveOffset` property of various
21 | objects. This will make it easier to interface with devices that do not place middle C at C4.
22 |
23 | ### Inbound Note Example
24 |
25 | If your external MIDI keyboard sends C3 and WEBMIDI.js reports it as C4, it is because your keyboard
26 | places middle C one octave lower than WEBMIDI.js does. To account for that, simply set
27 | [`WebMidi.octaveOffset`](/api/classes/WebMidi#octaveOffset) to `-1`. This way, when your keyboard
28 | sends C3, WEBMIDI.js will also report it as C3.
29 |
30 | In both cases the actual note number (60) remains the same. It is just being reported differently.
31 |
32 | ### Outbound Note Example
33 |
34 | If you are sending F#4 to an external device and that device thinks it's receiving F#5, it means
35 | that the external device places middle C one octave higher. In this case, set
36 | [`WebMidi.octaveOffset`](/api/classes/WebMidi#octaveOffset) to `1` to account for the difference.
37 |
38 | ## Offsetting Granularity
39 |
40 | For most scenarios, setting the global [`WebMidi.octaveOffset`](/api/classes/WebMidi#octaveOffset)
41 | is enough. However, the `octaveOffset` property is available for several objects to allow for better
42 | granularity:
43 |
44 | * [Input](/api/classes/Input)
45 | * [InputChannel](/api/classes/InputChannel)
46 | * [Output](/api/classes/Output)
47 | * [OutputChannel](/api/classes/OutputChannel)
48 | * [Note](/api/classes/Note)
49 | * [WebMidi](/api/classes/WebMidi)
50 |
51 | If you define `octaveOffset` on several objects, their value will be added. For example, if you
52 | set [`WebMidi.octaveOffset`](/api/classes/WebMidi#octaveOffset) to `-1` and set `octaveOffset` on a
53 | specific channel to `1`, the resulting offset on that channel will be `0` (-1 + 1) while the offset
54 | on other channels will be `1`.
55 |
--------------------------------------------------------------------------------
/website/docs/going-further/performance.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Performance
6 |
7 | ## Targeting Channels
8 |
9 | To complete.
10 |
11 | ## Disabling Validation
12 |
13 | To complete.
14 |
--------------------------------------------------------------------------------
/website/docs/going-further/sysex.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | title: Sysex
4 | ---
5 |
6 | # System Exclusive Messages (sysex)
7 |
--------------------------------------------------------------------------------
/website/docs/going-further/typescript.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | title: TypeScript
4 | ---
5 |
6 | # TypeScript
7 |
8 | TypeScript is supported in version 3+. However, it has not yet been tested extensively and some
9 | minor issues may remain.
10 |
11 | For instance, some types have been defined as `any` for lack of a better option. One such example
12 | has been described in
13 | [issue 229](https://github.com/djipco/webmidi/issues/229#issuecomment-1039036353). If you are a
14 | TypeScript expert, perhaps you can help.
15 |
--------------------------------------------------------------------------------
/website/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | slug: /
4 | ---
5 |
6 | # Quick Start For v3.x
7 |
8 | **You want to get started as quickly as possible?** This guide will let you establish a connection
9 | with your MIDI instrument in less than 5 minutes.
10 |
11 | :::info
12 |
13 | Documentation for [version 2.5.x](https://webmidijs.org/archives/api/v2/) and
14 | [version 1.0.0](http://webmidijs.org/archives/api/v1/classes/WebMidi.html) is also available.
15 |
16 | :::
17 |
18 | ## Step 1 - Create the HTML page
19 |
20 | :::tip
21 |
22 | Hint: You can **go even faster** by copying the
23 | [code](https://github.com/djipco/webmidi/blob/develop/examples/quick-start/index.html) from
24 | our GitHub repo.
25 |
26 | :::
27 |
28 | Create an HTML document and link to the library:
29 |
30 | ```html
31 |
32 |
33 |
34 |
35 |
36 |
37 | WebMidi.js Quick Start
38 |
39 |
40 |
41 |
42 | WebMidi.js Quick Start
43 |
44 |
45 |
46 | ```
47 |
48 | ## Step 2 - Add a script
49 |
50 | Add the following `
78 | ```
79 | ## Step 3 - Connect your device
80 |
81 | 🎹 Connect an input MIDI device (synth, drum machine, controller, etc.) and load the HTML page in a
82 | [compatible browser](/docs/getting-started#browser-support). You will be
83 | prompted to authorize the MIDI connection.
84 |
85 | After authorization, the page should detect the connected MIDI devices and display their name.
86 |
87 | :::info
88 |
89 | If nothing shows up, first make sure your MIDI device is detected at the operating system level.
90 |
91 | :::
92 |
93 | ## Step 4 - Listen for MIDI messages
94 |
95 | In the `onEnabled()` function, we first retrieve the input device we want to work with and store it
96 | in the `mySynth` variable. You can retrieve it by number or by name (as you wish).
97 |
98 | Then we use the `addListener()` method on MIDI channel 1 of the input device to add a
99 | callback function that will be called each time a **noteon** event is received on that MIDI channel.
100 |
101 | ```javascript
102 | function onEnabled() {
103 |
104 | if (WebMidi.inputs.length < 1) {
105 | document.body.innerHTML+= "No device detected.";
106 | } else {
107 | WebMidi.inputs.forEach((device, index) => {
108 | document.body.innerHTML+= `${index}: ${device.name} `;
109 | });
110 | }
111 |
112 | const mySynth = WebMidi.inputs[0];
113 | // const mySynth = WebMidi.getInputByName("TYPE NAME HERE!")
114 |
115 | mySynth.channels[1].addListener("noteon", e => {
116 | document.body.innerHTML+= `${e.note.name} `;
117 | });
118 |
119 | }
120 | ```
121 | Alternatively, if you wish to listen for notes from several channels at once, you can add the
122 | listener directly on the input device itself:
123 |
124 | ```javascript
125 | // Listen to 'note on' events on channels 1, 2 and 3 of the first input MIDI device
126 | WebMidi.inputs[0].addListener("noteon", e => {
127 | document.body.innerHTML+= `${e.note.name} `;
128 | }, {channels: [1, 2, 3]});
129 | ```
130 |
131 | ## Step 5 - Have fun!
132 |
133 | **That's it!** To go further, please take some time to check out the
134 | [Getting Started](getting-started) section. It covers important topics such as installation
135 | options, compatibility, security, etc.
136 |
137 | :::tip
138 |
139 | If you ever need further help, you can also head over to the
140 | [GitHub Discussions](https://github.com/djipco/webmidi/discussions) page and ask all the questions
141 | you want!
142 |
143 | :::
144 |
--------------------------------------------------------------------------------
/website/docs/migration/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Migration",
3 | "position": 30
4 | }
5 |
--------------------------------------------------------------------------------
/website/docs/roadmap/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Roadmap",
3 | "position": 50
4 | }
5 |
--------------------------------------------------------------------------------
/website/docs/roadmap/under-evaluation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: Under Evaluation
3 | sidebar_position: 2
4 | ---
5 |
6 | # Potential Enhancements To Evaluate
7 |
8 | The analysis has not started yet. We will wait after the official launch of version 3. We do have a
9 | lot of ideas and suggestions in store. Depending on whether these features break the API or not,
10 | they may make it into version 3.x or be deployed in v4.
11 |
12 | :::info
13 |
14 | If you have suggestions, please post them for discussion to the
15 | [Feature Request](https://github.com/djipco/webmidi/discussions/categories/feature-requests)
16 | category of our GitHub Discussions.
17 |
18 | :::
19 |
20 | ## Ideas & Suggestions to Evaluate
21 |
22 | If you feel any of these ideas should be given priority, plese explain why in the
23 | [Feature Request](https://github.com/djipco/webmidi/discussions/categories/feature-requests)
24 | category of our GitHub Discussions so I can properly triage them.
25 |
26 | * Add support for Web BLE MIDI ([browser implementation](https://github.com/skratchdot/ble-midi),
27 | [Node implementation](https://github.com/natcl/ble-midi))
28 |
29 | * Explore compatibility with WebMidiLink. Could we create an output that points to a WebMidiLinked
30 | device?
31 |
32 | * Could we allow `WebMidi.time` to be reset? (see
33 | [discussion #213](https://github.com/djipco/webmidi/discussions/213))
34 |
35 | * Add throttling or delay option to `sendSysex` (see discussion
36 | [#235](https://github.com/djipco/webmidi/discussions/235)).
37 |
38 | * Calculate BPM from clock messages
39 | ([Discussion #177](https://github.com/djipco/webmidi/discussions/177))
40 |
41 | * Allow the first argument of `output.playNote( )` to be ‘0:0’ as ‘A0’, ‘7:3’ as ‘E:3’ and so on.
42 |
43 | * Add a "mute" option for inputs/outputs
44 |
45 | * Include the ability to add MIDI event listeners at the WebMidi.js level
46 | ([Issue #138](https://github.com/djipco/webmidi/issues/138))
47 |
48 | * Emit events on `send()` so outbound MIDI messages can be listened for
49 | ([Discussion #171](https://github.com/djipco/webmidi/discussions/171))
50 |
51 | * Add a `stopAllNotes()` method
52 |
53 | * Calculate time values and make them directly available for `songposition` and `timecode` message
54 |
55 | * Make Istanbul (nyc) break down the coverage stats by test file.
56 |
57 | * Add the ability to send grouped messages for CC events (and potentially others)
58 |
59 | * Add expliocit support for
60 | [MIDI Polyphonic Expressions](https://www.midi.org/midi-articles/midi-polyphonic-expression-mpe).
61 |
62 | * Add explicit support for
63 | [Universal System Exclusive Messages](https://www.midi.org/specifications-old/item/table-4-universal-system-exclusive-messages)
64 |
65 | * This would include a `sendIdentityRequest()` method to the output object (perhaps with a
66 | `getIdentity()` companion method that waits for the response) ([Issue #117](https://github.com/djipco/webmidi/issues/117))
67 |
68 | * This could also include the capability to query device for make/model (similar to
69 | [jzz-midi-gear](https://www.npmjs.com/package/jzz-midi-gear))
70 |
71 | * Implement show control protocol subset
72 |
73 | * Add ability to inject Jazz-Plugin code for browsers with no native Web MIDI API support.
74 |
75 | * Add the option to create sysex plugins for various devices
76 | [forum thread](https://webmidijs.org/forum/discussion/comment/97#Comment_97)
77 |
78 | * Add
79 | [issue and PR templates](https://help.github.com/en/github/building-a-strong-community/about-issue-and-pull-request-templates)
80 |
81 | * Add continuous integration tool
82 |
83 | * Add ability to read/write MIDI files
84 |
85 | * Solid timing, midi clock, sync, transport functionality
86 |
87 | * Helper functions that help to deal with sysex checksum from specific manufacturer (Roland,
88 | checksum, etc.)
89 |
90 | * Add explicit support for Sample Dump Format (see discussion on
91 | [forum](https://webmidijs.org/forum/discussion/30/has-there-been-any-work-on-sample-dump-standard))
92 |
93 | * Allow third-party developers to develop modules that facilitate encoding and decoding of
94 | device-specific sysex messages (see [forum discussion](https://webmidijs.org/forum/discussion/37/))
95 |
96 | * Add timing capabilities such as syncing with Tone.js or being able to schedule events using
97 | musical notes.
98 |
99 | * Add the ability to export a MIDI file (perhaps with another lib such as
100 | [MidiWriterJS](https://www.npmjs.com/package/midi-writer-js) or
101 | [Jazz-Soft](https://jazz-soft.net/demo/WriteMidiFile.html)
102 |
103 | * SMF Support
104 |
105 | * Check if something specific needs to be done to support Electron
106 | ([this discussion](https://www.electronjs.org/docs/api/session#sessetpermissionrequesthandlerhandler)).
107 |
108 | * Evaluate whether if would be worth it to switch from the `midi` module to the `web-midi-test`
109 | module for unit tests (discussion [here](https://github.com/djipco/webmidi/discussions/223)).
110 |
111 | ## Enhancements Put On Hold For Now
112 |
113 | * Consider usage of
114 | [pipelining operator](https://github.com/tc39/proposal-pipeline-operator/blob/master/README.md#introduction)
115 | for patching webmidi function calls to a sequence
116 |
117 | * Consider using [middleware](https://github.com/unbug/js-middleware) approach for making the app
118 | pluggable
119 |
120 | * Investigate the possibility to add a `Device` object that would group inputs and outputs for a
121 | single device (see [discussion #280](https://github.com/djipco/webmidi/discussions/280) for
122 | details)
123 |
124 | * Piano roll
125 |
--------------------------------------------------------------------------------
/website/docs/roadmap/v4.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_label: Version 4
3 | sidebar_position: 2
4 | ---
5 |
6 | # Features Planned for Version 4
7 |
8 | The full analysis has not started yet. We do have a lot of [ideas](/docs/roadmap/under-evaluation)
9 | in store. Depending on whether these features break the API or not, they may make it into version
10 | 3.x or be deployed in v4.
11 |
12 | :::info
13 |
14 | If you have suggestions, please post them for discussion to the
15 | [Feature Request](https://github.com/djipco/webmidi/discussions/categories/feature-requests)
16 | category of our GitHub Discussions.
17 |
18 | :::
19 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docusaurus",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle",
10 | "deploy": "docusaurus deploy",
11 | "clear": "docusaurus clear",
12 | "serve": "docusaurus serve",
13 | "write-translations": "docusaurus write-translations",
14 | "write-heading-ids": "docusaurus write-heading-ids"
15 | },
16 | "dependencies": {
17 | "@docusaurus/core": "^3.0.0",
18 | "@docusaurus/plugin-client-redirects": "^3.0.0",
19 | "@docusaurus/plugin-content-blog": "^3.0.0",
20 | "@docusaurus/plugin-content-docs": "^3.0.0",
21 | "@docusaurus/preset-classic": "^3.0.0",
22 | "@docusaurus/theme-search-algolia": "^3.0.0",
23 | "@mdx-js/react": "^1.6.21",
24 | "@svgr/webpack": "^6.3.1",
25 | "clsx": "^1.1.1",
26 | "docusaurus-plugin-sass": "^0.2.5",
27 | "file-loader": "^6.2.0",
28 | "prism-react-renderer": "^1.2.1",
29 | "react": "^17.0.1",
30 | "react-dom": "^17.0.1",
31 | "react-helmet": "^6.1.0",
32 | "sass": "^1.43.4",
33 | "typescript": "^4.4.2",
34 | "url-loader": "^4.1.1"
35 | },
36 | "browserslist": {
37 | "production": [
38 | ">0.5%",
39 | "not dead",
40 | "not op_mini all"
41 | ],
42 | "development": [
43 | "last 1 chrome version",
44 | "last 1 firefox version",
45 | "last 1 safari version"
46 | ]
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/website/sidebars.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Creating a sidebar enables you to:
3 | - create an ordered group of docs
4 | - render a sidebar for each doc of that group
5 | - provide next/previous navigation
6 |
7 | The sidebars can be generated from the filesystem, or explicitly defined here.
8 |
9 | Create as many sidebars as you want.
10 | */
11 |
12 | module.exports = {
13 | // By default, Docusaurus generates a sidebar from the docs folder structure
14 | tutorialSidebar: [{type: "autogenerated", dirName: "."}],
15 |
16 | // But you can create a sidebar manually
17 | /*
18 | tutorialSidebar: [
19 | {
20 | type: 'category',
21 | label: 'Tutorial',
22 | items: ['hello'],
23 | },
24 | ],
25 | */
26 | };
27 |
--------------------------------------------------------------------------------
/website/src/components/Button.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from "./Button.module.scss";
3 |
4 | /*export default function Button({children, type, href, target}) {
5 | return (
6 |
10 | );
11 | }*/
12 |
13 | export default function Button(props) {
14 | const component = "Button";
15 | const{
16 | children,
17 | href,
18 | type,
19 | target,
20 | } = props;
21 | return (
22 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/website/src/components/Button.module.css:
--------------------------------------------------------------------------------
1 | .Button {
2 | background-color: var(--color-accent);
3 | width: fit-content;
4 | height: fit-content;
5 | border-radius: 10px;
6 | position: relative;
7 | overflow: hidden;
8 | text-align: center;
9 | }
10 | .Button a {
11 | display: block;
12 | font: bold 1.56rem var(--font-secondary), sans-serif;
13 | padding: var(--spacing-sm) 1.5em;
14 | color: var(--color-text-primary);
15 | text-decoration: none;
16 | position: relative;
17 | z-index: 2;
18 | }
19 | .Button .buttonBg {
20 | width: 100%;
21 | height: 100%;
22 | position: absolute;
23 | }
24 | .Button.button-bg-full {
25 | border: solid 4px var(--color-accent);
26 | }
27 | .Button.button-bg-full .buttonBg {
28 | background-color: var(--color-accent-lighter);
29 | z-index: 1;
30 | top: 0;
31 | left: -100%;
32 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
33 | }
34 | .Button.button-bg-full:hover {
35 | border: solid 4px var(--color-accent-lighter);
36 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
37 | }
38 | .Button.button-bg-full:hover .buttonBg {
39 | left: 0;
40 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
41 | }
42 | .Button.button-bg-empty {
43 | background-color: rgba(0, 0, 0, 0);
44 | border: solid 4px var(--color-accent);
45 | }
46 | .Button.button-bg-empty a {
47 | color: var(--color-accent);
48 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
49 | }
50 | .Button.button-bg-empty .buttonBg {
51 | background-color: var(--color-accent-lighter);
52 | z-index: 1;
53 | top: 0;
54 | left: -100%;
55 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
56 | }
57 | .Button.button-bg-empty:hover {
58 | border: solid 4px var(--color-accent-lighter);
59 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
60 | }
61 | .Button.button-bg-empty:hover a {
62 | color: var(--color-white);
63 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
64 | }
65 | .Button.button-bg-empty:hover .buttonBg {
66 | left: 0;
67 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
68 | }
69 |
70 | /*# sourceMappingURL=Button.module.css.map */
71 |
--------------------------------------------------------------------------------
/website/src/components/Button.module.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["Button.module.scss"],"names":[],"mappings":"AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EAEA;EACA;;AAEF;EACE;EACA;EACA;;AAEF;EACE;;AACA;EACE;EAEA;EACA;EACA;EACA;;AAEF;EACE;EACA;;AACA;EACE;EACA;;AAIN;EACE;EACA;;AACA;EACE;EACA;;AAEF;EACE;EAEA;EACA;EACA;EACA;;AAEF;EACE;EACA;;AACA;EACE;EACA;;AAEF;EACE;EACA","file":"Button.module.css"}
--------------------------------------------------------------------------------
/website/src/components/Button.module.scss:
--------------------------------------------------------------------------------
1 | $ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
2 | .Button {
3 | background-color: var(--color-accent);
4 | width: fit-content;
5 | height: fit-content;
6 | border-radius: 10px;
7 | position: relative;
8 | overflow: hidden;
9 | text-align: center;
10 |
11 | a {
12 | display: block;
13 | font: bold 1.56rem var(--font-secondary), sans-serif;
14 | padding: var(--spacing-sm) 1.5em;
15 | color: var(--color-text-primary);
16 | text-decoration: none;
17 |
18 | position: relative;
19 | z-index: 2;
20 | }
21 | .buttonBg {
22 | width: 100%;
23 | height: 100%;
24 | position: absolute;
25 | }
26 | &.button-bg-full {
27 | border: solid 4px var(--color-accent);
28 | .buttonBg {
29 | background-color: var(--color-accent-lighter);
30 |
31 | z-index: 1;
32 | top: 0;
33 | left: -100%;
34 | transition: all 0.3s $ease-in-out-circ;
35 | }
36 | &:hover {
37 | border: solid 4px var(--color-accent-lighter);
38 | transition: all 0.3s $ease-in-out-circ;
39 | .buttonBg {
40 | left: 0;
41 | transition: all 0.3s $ease-in-out-circ;
42 | }
43 | }
44 | }
45 | &.button-bg-empty {
46 | background-color: rgba(0, 0, 0, 0%);
47 | border: solid 4px var(--color-accent);
48 | a {
49 | color: var(--color-accent);
50 | transition: all 0.3s $ease-in-out-circ;
51 | }
52 | .buttonBg {
53 | background-color: var(--color-accent-lighter);
54 |
55 | z-index: 1;
56 | top: 0;
57 | left: -100%;
58 | transition: all 0.3s $ease-in-out-circ;
59 | }
60 | &:hover {
61 | border: solid 4px var(--color-accent-lighter);
62 | transition: all 0.3s $ease-in-out-circ;
63 | a {
64 | color: var(--color-white);
65 | transition: all 0.3s $ease-in-out-circ;
66 | }
67 | .buttonBg {
68 | left: 0;
69 | transition: all 0.3s $ease-in-out-circ;
70 | }
71 | }
72 | }
73 | }
74 |
75 |
76 |
--------------------------------------------------------------------------------
/website/src/components/Column.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from "./Column.module.scss";
3 |
4 | export default function Column({children, type,}) {
5 | const component = "Column";
6 | return (
7 |
12 | {children}
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/website/src/components/Column.module.css:
--------------------------------------------------------------------------------
1 | .Column {
2 | display: grid;
3 | }
4 |
5 | .col-2 {
6 | grid-template-columns: 48% 48%;
7 | justify-content: space-between;
8 | align-items: center;
9 | }
10 | @media screen and (max-width: 1000px) {
11 | .col-2 {
12 | display: grid;
13 | grid-template-columns: 1fr;
14 | text-align: center;
15 | }
16 | }
17 |
18 | /*# sourceMappingURL=Column.module.css.map */
19 |
--------------------------------------------------------------------------------
/website/src/components/Column.module.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["Column.module.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;;AAEA;EALF;IAMI;IACA;IACA","file":"Column.module.css"}
--------------------------------------------------------------------------------
/website/src/components/Column.module.scss:
--------------------------------------------------------------------------------
1 | .Column{
2 | display: grid;
3 | }
4 |
5 | .col-2 {
6 | grid-template-columns: 48% 48%;
7 | justify-content: space-between;
8 | align-items: center;
9 |
10 | @media screen and(max-width: 1000px){
11 | display: grid;
12 | grid-template-columns: 1fr;
13 | text-align: center;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/website/src/components/HomepageFeatures.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import clsx from "clsx";
3 | import styles from "./HomepageFeatures.module.css";
4 |
5 | const FeatureList = [
6 | // {
7 | // title: 'Easy to Use',
8 | // Svg: require('../../static/img/undraw_docusaurus_mountain.svg').default,
9 | // description: (
10 | // <>
11 | // Docusaurus was designed from the ground up to be easily installed and
12 | // used to get your website up and running quickly.
13 | // >
14 | // ),
15 | // },
16 | // {
17 | // title: 'Focus on What Matters',
18 | // Svg: require('../../static/img/undraw_docusaurus_tree.svg').default,
19 | // description: (
20 | // <>
21 | // Docusaurus lets you focus on your docs, and we'll do the chores. Go
22 | // ahead and move your docs into the docs
directory.
23 | // >
24 | // ),
25 | // },
26 | // {
27 | // title: 'Powered by React',
28 | // Svg: require('../../static/img/undraw_docusaurus_react.svg').default,
29 | // description: (
30 | // <>
31 | // Extend or customize your website layout by reusing React. Docusaurus can
32 | // be extended while reusing the same header and footer.
33 | // >
34 | // ),
35 | // },
36 | ];
37 |
38 | function Feature({Svg, title, description}) {
39 | return (
40 |
41 |
42 |
43 |
44 |
45 |
{title}
46 |
{description}
47 |
48 |
49 | );
50 | }
51 |
52 | export default function HomepageFeatures() {
53 | return (
54 |
55 |
56 |
57 | {FeatureList.map((props, idx) => (
58 |
59 | ))}
60 |
61 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/website/src/components/HomepageFeatures.module.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable docusaurus/copyright-header */
2 |
3 | .features {
4 | display: flex;
5 | align-items: center;
6 | padding: 2rem 0;
7 | width: 100%;
8 | }
9 |
10 | .featureSvg {
11 | height: 200px;
12 | width: 200px;
13 | }
14 |
--------------------------------------------------------------------------------
/website/src/components/InformationBar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from "./InformationBar.module.scss";
3 |
4 | export default function InformationBar({children, type,}) {
5 | const component = "InformationBar";
6 | return (
7 |
12 |
13 |
14 | {children}
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/website/src/components/InformationBar.module.css:
--------------------------------------------------------------------------------
1 | .InformationBar {
2 | background-color: var(--color-bg-secondary);
3 | padding: var(--spacing-xl) 0;
4 | color: var(--color-white);
5 | text-align: center;
6 | }
7 | .InformationBar p {
8 | font-size: 1.87rem;
9 | line-height: 120%;
10 | margin: 0;
11 | }
12 |
13 | /*# sourceMappingURL=InformationBar.module.css.map */
14 |
--------------------------------------------------------------------------------
/website/src/components/InformationBar.module.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["InformationBar.module.scss"],"names":[],"mappings":"AAAA;EACI;EACA;EACA;EACF;;AACE;EACE;EACA;EACA","file":"InformationBar.module.css"}
--------------------------------------------------------------------------------
/website/src/components/InformationBar.module.scss:
--------------------------------------------------------------------------------
1 | .InformationBar{
2 | background-color: var(--color-bg-secondary);
3 | padding: var(--spacing-xl) 0;
4 | color: var(--color-white);
5 | text-align: center;
6 | p {
7 | font-size: 1.87rem;
8 | line-height: 120%;
9 | margin:0;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/website/src/css/custom.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["custom.scss"],"names":[],"mappings":"AAAA;AAMQ;AAGR;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAGA;EACA;EAGA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EAEA;EAGA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAvDF;IAwDI;;;;AAIF;EACE;EACA;EAEA;EACA;EACA;;;AASJ;EACE;EACA;EACA;EACA;;;AAIF;EAEE;;AAEA;EACE;;;AAKJ;EACE;;;AAIF;EACE;;;AAGF;EACE;;;AAGF;EAEE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAIF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAMF;EACE;;;AAIF;EACE;;;AAKF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAMA;EAEE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAKN;AAAA;AAAA;AAIA;EACE;EACA;EACA;;AAEA;EALF;IAMI;;;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EANF;IAOI;;;;AAIJ;EACE;EACA;EACA;;AAEA;EALF;IAMI;;;;AAKJ;EACE;;;AAIF;EACE;EACA;EACA","file":"custom.css"}
--------------------------------------------------------------------------------
/website/src/css/index.css:
--------------------------------------------------------------------------------
1 | .hero {
2 | padding: var(--spacing-lg) 0;
3 | min-height: 60vh;
4 | color: var(--color-text-primary);
5 | display: flex;
6 | align-items: center;
7 | background: var(--color-bg-primary);
8 | text-align: center;
9 | }
10 | @media screen and (max-width: 1000px) {
11 | .hero {
12 | min-height: 40vh;
13 | padding-top: 0;
14 | }
15 | }
16 | .hero .logo {
17 | max-width: 600px;
18 | height: 300px;
19 | background: center url("/img/webmidijs-logo-dark.svg") no-repeat;
20 | margin: auto;
21 | }
22 | @media screen and (max-width: 500px) {
23 | .hero .logo {
24 | height: 10em;
25 | margin: var(--spacing-md) 0;
26 | }
27 | }
28 | html[data-theme=dark] .hero .logo {
29 | background: center url("/img/webmidijs-logo-light.svg") no-repeat;
30 | }
31 | .hero span {
32 | font-family: var(--font-secondary);
33 | font-size: 2rem;
34 | display: block;
35 | margin-bottom: var(--spacing-sm);
36 | font-weight: bold;
37 | color: var(--color-accent);
38 | }
39 | .hero .cta {
40 | display: flex;
41 | flex-wrap: wrap;
42 | justify-content: center;
43 | /*@media screen and(max-width: 1000px){
44 | justify-content: center;
45 |
46 | }*/
47 | }
48 | .hero .cta .Button {
49 | margin: var(--spacing-sm) 0 0 var(--spacing-sm);
50 | }
51 | .hero .cta .Button:nth-child(1) {
52 | margin-left: 0;
53 | }
54 | .hero .img {
55 | margin: auto;
56 | }
57 | @media screen and (max-width: 1000px) {
58 | .hero .texts {
59 | order: 2;
60 | }
61 | }
62 |
63 | /*======== Presentation ========*/
64 | .presentation {
65 | margin: var(--spacing-lg) 0;
66 | }
67 | .presentation h2 {
68 | text-align: center;
69 | }
70 | .presentation p {
71 | font-size: 1.5rem;
72 | margin: var(--spacing-sm) 0;
73 | }
74 | .presentation .media {
75 | background-color: var(--color-bg-tertiary);
76 | border-radius: 20px;
77 | width: 100%;
78 | height: 375px;
79 | display: flex;
80 | justify-content: center;
81 | align-items: center;
82 | margin: var(--spacing-xl) auto;
83 | box-shadow: 10px 10px 0 rgba(255, 208, 0, 0.5);
84 | }
85 | @media screen and (max-width: 1000px) {
86 | .presentation .media {
87 | width: 90%;
88 | margin: var(--spacing-md) auto;
89 | }
90 | }
91 | .presentation .media .imgMedia {
92 | width: 80%;
93 | height: 80%;
94 | }
95 | html[data-theme=light] .presentation .media .imgMedia {
96 | filter: invert(1);
97 | }
98 | .presentation .media :nth-child(1) {
99 | background: center url("/img/front-page/webmidi-demonstration.svg") no-repeat;
100 | }
101 | @media screen and (max-width: 1000px) {
102 | .presentation .media :nth-child(1) {
103 | background: center url("/img/front-page/webmidi-demonstration-vertical.svg") no-repeat;
104 | }
105 | }
106 |
107 | /*# sourceMappingURL=index.css.map */
108 |
--------------------------------------------------------------------------------
/website/src/css/index.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["index.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EAEA;;AAEA;EAVF;IAWI;IACA;;;AAIF;EACE;EACA;EACA;EACA;;AAEA;EANF;IAOI;IACA;;;AAGF;EACE;;AAGJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EAEA;AAQA;AAAA;;AAAA;;AANA;EACE;;AAEF;EACE;;AAOJ;EACE;;AAGA;EADF;IAEI;;;;AAIN;AACA;EACE;;AACA;EACE;;AAEF;EACE;EACA;;AAEF;EACE;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;EAEA;;AAEA;EAfF;IAgBI;IACA;;;AAGF;EACE;EACA;;AAEA;EACE;;AAKN;EACE;;AACA;EAFF;IAGI","file":"index.css"}
--------------------------------------------------------------------------------
/website/src/css/index.scss:
--------------------------------------------------------------------------------
1 | .hero{
2 | padding: var(--spacing-lg) 0;
3 | min-height: 60vh;
4 | color: var(--color-text-primary);
5 | display: flex;
6 | align-items: center;
7 | background: var(--color-bg-primary);
8 |
9 | text-align: center;
10 |
11 | @media screen and(max-width: 1000px){
12 | min-height: 40vh;
13 | padding-top: 0;
14 | }
15 |
16 |
17 | .logo {
18 | max-width: 600px;
19 | height: 300px;
20 | background: center url("/img/webmidijs-logo-dark.svg") no-repeat;
21 | margin: auto;
22 |
23 | @media screen and(max-width: 500px){
24 | height: 10em;
25 | margin: var(--spacing-md) 0;
26 | }
27 |
28 | html[data-theme=dark] &{
29 | background: center url("/img/webmidijs-logo-light.svg") no-repeat;
30 | }
31 | }
32 | span {
33 | font-family: var(--font-secondary);
34 | font-size: 2rem;
35 | display: block;
36 | margin-bottom: var(--spacing-sm);
37 | font-weight: bold;
38 | color: var(--color-accent);
39 | }
40 |
41 | .cta {
42 | display: flex;
43 | flex-wrap: wrap;
44 |
45 | justify-content: center;
46 |
47 | .Button{
48 | margin: var(--spacing-sm) 0 0 var(--spacing-sm);
49 | }
50 | .Button:nth-child(1){
51 | margin-left: 0;
52 | }
53 | /*@media screen and(max-width: 1000px){
54 | justify-content: center;
55 |
56 | }*/
57 | }
58 | .img{
59 | margin: auto;
60 | }
61 | .texts{
62 | @media screen and(max-width: 1000px){
63 | order: 2;
64 | }
65 | }
66 | }
67 | /*======== Presentation ========*/
68 | .presentation {
69 | margin: var(--spacing-lg) 0;
70 | h2 {
71 | text-align: center;
72 | }
73 | p{
74 | font-size: 1.5rem;
75 | margin: var(--spacing-sm) 0;
76 | }
77 | .media {
78 | background-color: var(--color-bg-tertiary);
79 | border-radius: 20px;
80 |
81 | width: 100%;
82 | height: 375px;
83 |
84 | display: flex;
85 | justify-content: center;
86 | align-items: center;
87 |
88 | margin: var(--spacing-xl) auto;
89 |
90 | box-shadow: 10px 10px 0 rgba(255, 208, 0, 0.5);
91 |
92 | @media screen and(max-width: 1000px){
93 | width: 90%;
94 | margin: var(--spacing-md) auto;
95 | }
96 |
97 | .imgMedia{
98 | width: 80%;
99 | height: 80%;
100 |
101 | html[data-theme=light] &{
102 | filter: invert(1);
103 | }
104 | }
105 | }
106 |
107 | .media :nth-child(1){
108 | background: center url("/img/front-page/webmidi-demonstration.svg") no-repeat;
109 | @media screen and(max-width: 1000px){
110 | background: center url("/img/front-page/webmidi-demonstration-vertical.svg") no-repeat;
111 | }
112 | }
113 | }
114 |
115 |
116 |
--------------------------------------------------------------------------------
/website/src/pages/about/index.md:
--------------------------------------------------------------------------------
1 | # About
2 |
3 | ## Who created this?
4 |
5 | **WEBMIDI.js** is a passion project of mine. I am Jean-Philippe Côté (a.k.a.
6 | [djip.co](https://djip.co)), an
7 | [academic](https://www.cegepmontpetit.ca/cegep/recherche/professeurs-chercheurs/jean-philippe-cote)
8 | and artist with particular interests in creative coding, interactive arts and music technology. You
9 | can reach out to me in different ways:
10 |
11 | * Twitter: **[@djipco](https://twitter.com/djipco)** or **[@webmidijs](https://twitter.com/webmidijs)**
12 | * Website: **[https://djip.co/](https://djip.co)**
13 | * GitHub: **[https://github.com/djipco/](https://github.com/djipco)**
14 |
15 | One of my students, **Jean-Marie Gariépy** has also been helping out in various capacities with the
16 | creation of this website. Let's all thank him for his contribution! 👏
17 |
18 | ## Sponsoring the project
19 |
20 | You can [sponsor the project](https://github.com/sponsors/djipco/) by becoming a GitHub sponsor. All
21 | you need is a GitHub account. If you see value in this library, please consider a contribution. 🙏🏻
22 |
23 | ## Licensing
24 |
25 | Starting with version 3.0.0, this library is licensed under the **Apache License, Version 2.0**. You
26 | may not use this library except in compliance with this license. You may obtain a copy at:
27 |
28 | * [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
29 |
30 | Unless required by applicable law or agreed to in writing, software distributed under this license
31 | is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
32 | implied. See the above license for the specific language governing permissions and limitations.
33 |
34 | © 2015-2023, Jean-Philippe Côté.
35 |
--------------------------------------------------------------------------------
/website/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import useBaseUrl from '@docusaurus/useBaseUrl';
3 | import Layout from "@theme/Layout";
4 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
5 | import Button from "../components/Button";
6 | import Column from "../components/Column";
7 | import InformationBar from "../components/InformationBar";
8 |
9 | function HomepageHero() {
10 | const {siteConfig} = useDocusaurusContext();
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
{siteConfig.tagline}
21 |
22 | Get started in 5 minutes!
27 |
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
35 | function Presentation() {
36 | const {siteConfig} = useDocusaurusContext();
37 | return (
38 |
39 |
40 |
What is {siteConfig.title}?
41 |
44 |
45 | The existing Web MIDI API is a really exciting addition to the web platform
46 | allowing a web page to interact with MIDI musical instruments .
47 | However, while great, most developers will find the original API to be
48 | too low-level for their needs. Having to perform binary arithmetic
49 | or needing to read the 300-page MIDI spec is no fun (trust us on this!).
50 | The goal behind WEBMIDI.js is to get you started with your web-based
51 | MIDI project as efficiently as possible.
52 |
53 |
54 |
61 |
62 |
63 |
64 |
65 | );
66 | }
67 |
68 | export default function Home() {
69 | const {siteConfig} = useDocusaurusContext();
70 | return (
71 |
74 |
75 |
76 |
77 | Version 3.0 has been released!
78 | Subscribe to the newsletter
82 |
83 | to learn about all the new features.
84 |
85 |
86 |
87 |
88 | );
89 | }
90 |
--------------------------------------------------------------------------------
/website/src/pages/index.module.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable docusaurus/copyright-header */
2 | /**
3 | * CSS files with the .module.css suffix will be treated as CSS modules
4 | * and scoped locally.
5 | */
6 | /*
7 | .heroBanner {
8 | padding: 4rem 0;
9 | text-align: center;
10 | position: relative;
11 | overflow: hidden;
12 | }
13 |
14 | @media screen and (max-width: 966px) {
15 | .heroBanner {
16 | padding: 2rem;
17 | }
18 | }
19 |
20 | .buttons {
21 | display: flex;
22 | align-items: center;
23 | justify-content: center;
24 | }
25 | */
26 | .hero {
27 | padding: var(--spacing-lg);
28 | min-height: 60vh;
29 | display: flex;
30 | align-items: center;
31 | }
32 | .hero h1 {
33 | margin: 0;
34 | text-transform: uppercase;
35 | }
36 | .hero span {
37 | font-family: var(--font-secondary);
38 | font-size: 1.56rem;
39 | display: block;
40 | margin-bottom: var(--spacing-sm);
41 | }
42 | .hero .cta {
43 | display: flex;
44 | }
45 | .hero .cta .button:nth-child(n) {
46 | margin-left: var(--spacing-md);
47 | }
48 | .hero .cta .button:first-child {
49 | margin-left: 0;
50 | }
51 |
52 | /*======== Boutons ========*/
53 | .button {
54 | background-color: var(--color-accent);
55 | width: fit-content;
56 | border-radius: 10px;
57 | position: relative;
58 | overflow: hidden;
59 | }
60 | .button a {
61 | display: block;
62 | padding: var(--spacing-sm) var(--spacing-lg);
63 | font-weight: bold;
64 | font-family: var(--font-secondary);
65 | font-size: 1.56rem;
66 | position: relative;
67 | z-index: 2;
68 | }
69 | .button .button--bg {
70 | width: 100%;
71 | height: 100%;
72 | position: absolute;
73 | }
74 | .button.button-bg-full .button--bg {
75 | background-color: var(--color-accent-lighter);
76 | z-index: 1;
77 | top: 0;
78 | left: -100%;
79 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
80 | }
81 | .button.button-bg-full:hover .button--bg {
82 | left: 0;
83 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
84 | }
85 | .button.button-bg-empty {
86 | background-color: rgba(0, 0, 0, 0);
87 | border: solid 4px var(--color-accent);
88 | }
89 | .button.button-bg-empty a {
90 | color: var(--color-accent);
91 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
92 | }
93 | .button.button-bg-empty .button--bg {
94 | background-color: var(--color-accent-lighter);
95 | z-index: 1;
96 | top: 0;
97 | left: -100%;
98 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
99 | }
100 | .button.button-bg-empty:hover a {
101 | color: var(--color-white);
102 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
103 | }
104 | .button.button-bg-empty:hover .button--bg {
105 | left: 0;
106 | transition: all 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
107 | }
108 |
109 | /*======== Columns ========*/
110 | .col-2 {
111 | display: grid;
112 | grid-template-columns: 48% 48%;
113 | justify-content: space-between;
114 | align-items: center;
115 | }
116 |
117 | /*# sourceMappingURL=index.module.css.map */
118 |
--------------------------------------------------------------------------------
/website/src/pages/index.module.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["index.module.scss"],"names":[],"mappings":"AAAA;AAEA;AAAA;AAAA;AAAA;AAIA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBA;EACE;EACA;EAEA;EACA;;AACA;EACE;EACA;;AAEF;EACE;EACA;EACA;EACA;;AAGF;EACE;;AACA;EACE;;AAEF;EACE;;;AAKN;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EAEA;EACA;;AAEF;EACE;EACA;EACA;;AAGA;EACE;EAEA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAIN;EACE;EACA;;AACA;EACE;EACA;;AAEF;EACE;EAEA;EACA;EACA;EACA;;AAGA;EACE;EACA;;AAEF;EACE;EACA;;;AAOR;AACA;EACE;EACA;EACA;EACA","file":"index.module.css"}
--------------------------------------------------------------------------------
/website/src/pages/index.module.scss:
--------------------------------------------------------------------------------
1 | /* stylelint-disable docusaurus/copyright-header */
2 |
3 | /**
4 | * CSS files with the .module.css suffix will be treated as CSS modules
5 | * and scoped locally.
6 | */
7 | /*
8 | .heroBanner {
9 | padding: 4rem 0;
10 | text-align: center;
11 | position: relative;
12 | overflow: hidden;
13 | }
14 |
15 | @media screen and (max-width: 966px) {
16 | .heroBanner {
17 | padding: 2rem;
18 | }
19 | }
20 |
21 | .buttons {
22 | display: flex;
23 | align-items: center;
24 | justify-content: center;
25 | }
26 | */
27 | .hero{
28 | padding: var(--spacing-lg);
29 | min-height: 60vh;
30 |
31 | display: flex;
32 | align-items: center;
33 | h1 {
34 | margin: 0;
35 | text-transform: uppercase;
36 | }
37 | span {
38 | font-family: var(--font-secondary);
39 | font-size: 1.56rem;
40 | display: block;
41 | margin-bottom: var(--spacing-sm);
42 | }
43 |
44 | .cta {
45 | display: flex;
46 | .button:nth-child(n) {
47 | margin-left: var(--spacing-md);
48 | }
49 | .button:first-child {
50 | margin-left: 0;
51 | }
52 | }
53 | }
54 |
55 | /*======== Boutons ========*/
56 | $ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
57 | .button {
58 | background-color: var(--color-accent);
59 | width: fit-content;
60 | border-radius: 10px;
61 | position: relative;
62 | overflow: hidden;
63 |
64 | a {
65 | display: block;
66 | padding: var(--spacing-sm) var(--spacing-lg);
67 | font-weight: bold;
68 | font-family: var(--font-secondary);
69 | font-size: 1.56rem;
70 |
71 | position: relative;
72 | z-index: 2;
73 | }
74 | .button--bg {
75 | width: 100%;
76 | height: 100%;
77 | position: absolute;
78 | }
79 | &.button-bg-full {
80 | .button--bg {
81 | background-color: var(--color-accent-lighter);
82 |
83 | z-index: 1;
84 | top: 0;
85 | left: -100%;
86 | transition: all 0.3s $ease-in-out-circ;
87 | }
88 | &:hover {
89 | .button--bg {
90 | left: 0;
91 | transition: all 0.3s $ease-in-out-circ;
92 | }
93 | }
94 | }
95 | &.button-bg-empty {
96 | background-color: rgba(0, 0, 0, 0%);
97 | border: solid 4px var(--color-accent);
98 | a {
99 | color: var(--color-accent);
100 | transition: all 0.3s $ease-in-out-circ;
101 | }
102 | .button--bg {
103 | background-color: var(--color-accent-lighter);
104 |
105 | z-index: 1;
106 | top: 0;
107 | left: -100%;
108 | transition: all 0.3s $ease-in-out-circ;
109 | }
110 | &:hover {
111 | a {
112 | color: var(--color-white);
113 | transition: all 0.3s $ease-in-out-circ;
114 | }
115 | .button--bg {
116 | left: 0;
117 | transition: all 0.3s $ease-in-out-circ;
118 | }
119 | }
120 | }
121 |
122 | }
123 |
124 | /*======== Columns ========*/
125 | .col-2 {
126 | display: grid;
127 | grid-template-columns: 48% 48%;
128 | justify-content: space-between;
129 | align-items: center;
130 | }
131 |
132 |
133 |
--------------------------------------------------------------------------------
/website/src/pages/research/index.md:
--------------------------------------------------------------------------------
1 | # Academic Research
2 |
3 | I invite all academics and researchers to show their support for this project by properly citing it
4 | wherever appropriate in your publications and references.
5 |
6 | ## Citing this Software
7 |
8 | I wrote a
9 | [paper about WEBMIDI.js](https://nime.pubpub.org/pub/user-friendly-midi-in-the-web-browser) and,
10 | more specifically, about how it tries to address the usability shortcomings of the Web MIDI API. I
11 | invite academics to cite it in their publication whenever appropriate:
12 |
13 | > Côté, J.-P. (2022). User-Friendly MIDI in the Web Browser. NIME 2022.
14 | > https://doi.org/10.21428/92fbeb44.388e4764
15 |
16 | You can also cite the library itself like so (APA style):
17 |
18 | > Côté, J. P. (2023). WEBMIDI.js v3.1.6 [Computer Software]. Retrieved from
19 | > https://github.com/djipco/webmidi
20 |
21 | ## Papers Citing Usage of WEBMIDI.js
22 |
23 | * Leischner, V., & Husa, P. (2023). Sonification of a Juggling Performance Using Spatial Audio.
24 | Proceedings of the ACM on Computer Graphics and Interactive Techniques, 6(2), 1–6.
25 | https://doi.org/10.1145/3597619
26 |
27 | * Baratè, A., & Ludovico, L. A. (2022). **Web MIDI API: State of the Art and Future Perspectives**.
28 | Journal of the Audio Engineering Society, 70(11), 918–925.
29 | https://www.aes.org/e-lib/browse.cfm?elib=22016
30 |
31 | * Graber, Z., & Henrion, W. (2021). **Music For Me**. Computer Science and Engineering Senior Theses.
32 | https://scholarcommons.scu.edu/cseng_senior/203
33 |
34 | * Kostek, B. (Éd.). (2021). **Postępy badań w inżynierii dźwięku i obrazu**. Politechnika Wrocławska,
35 | Oficyna Wydawnicza. https://doi.org/10.37190/ido2021
36 |
37 | * Walczak, M., & Łukasik, E. (2021). **Rozproszony system generowania, edycji i transmisji dźwięku
38 | wykorzystujący interfejsy Web Audio API, WebRTC i Web MIDI API**. In Postępy badań w inżynierii
39 | dźwięku i obrazu: Nowe trendy i zastosowania technologii dźwięku wielokanałowego oraz badania
40 | jakości dźwięku (pp. 83–104). Oficyna Wydawnicza Politechniki Wrocławskiej.
41 | https://doi.org/10.37190/ido2021
42 |
43 | * Krawczuk, J. (2020). **Real-Time and Post-Hoc-Visualizations of Guitar Performances as a Support
44 | for Music Education**. https://doi.org/10/gkb622
45 |
46 | * Lundh Haaland, M. (2020). **The Player as a Conductor: Utilizing an Expressive Performance
47 | System to Create an Interactive Video Game Soundtrack** (Dissertation). Retrieved from
48 | http://urn.kb.se/resolve?urn=urn:nbn:se:kth:diva-281324
49 |
50 | * Bazin, T. & Hadjeres, G. (2019). **NONOTO: A Model-agnostic Web Interface for Interactive
51 | Music Composition by Inpainting**, presented at 10th International Conference on Computational
52 | Creativity, Charlotte, 2019. Retrieved from https://arxiv.org/abs/1907.10380
53 |
54 | * Smith, A. (2019). **Sonification: Turning the Yield Curve into Music**. FT.com.
55 | http://search.proquest.com/docview/2191715473/fulltext/3D4C05EAFC6A4AEEPQ/1?accountid=14719
56 |
57 | * Cárdenas, A. & Mauricio B. (2018). **Diseño y desarrollo de un prototipo para integración de
58 | Tecnología de Tracking 3d con Tecnología MIDI** [Doctoral dissertation, Pontificia Universidad
59 | Católica del Ecuador]. Retrieved from http://repositorio.puce.edu.ec/handle/22000/15838
60 |
61 | If you are using WEBMIDI.js in your research, I would love to know about it. To notify me, you can
62 | simply drop me a note on [Twitter](https://twitter.com/djipco). By the way, I'm open to artistic
63 | and/or academic collaborations.
64 |
--------------------------------------------------------------------------------
/website/src/pages/sponsors/index.md:
--------------------------------------------------------------------------------
1 | # Sponsors
2 |
3 | :::tip Consider a sponsorship
4 |
5 | Please help me grow and nurture WEBMIDI.js by ❤️ [sponsoring](https://github.com/sponsors/djipco)
6 | the project on GitHub.
7 |
8 | :::
9 |
10 | ## Sponsoring Organizations
11 | ---
12 |
13 |
14 |
15 |
16 |
17 | ## Sponsors
18 | ---
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | ## About Software Development...
29 |
30 | As you surely know, proper software development takes time. This time is spent on coding new
31 | features but also on various other important tasks such as writing useful documentation, answering
32 | questions, reviewing issues, fixing bugs, writing tests, etc.
33 |
34 | While WEBMIDI.js started as a hobby project, it is quietly becoming the "go to" library for MIDI on
35 | the web. Hopefully, your contributions will make allow me to continue developing, maintaining and
36 | advocating for this project that I cherish.
37 |
38 | Thank you so much. 😀
39 |
--------------------------------------------------------------------------------
/website/src/pages/tester/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Layout from "@theme/Layout";
3 | import {Helmet} from "react-helmet";
4 |
5 |
6 |
7 | // import useBaseUrl from "@docusaurus/useBaseUrl";
8 | // import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
9 |
10 | // const piano = new Nexus.Piano("#target",{
11 | // size: [500,125],
12 | // mode: "button", // "button", "toggle", or "impulse"
13 | // lowNote: 24,
14 | // highNote: 60
15 | // })
16 |
17 | function Tester() {
18 | return (
19 |
20 |
21 |
22 | {/*
*/}
23 |
24 |
25 |
33 |
34 | Edit pages/helloReact.js
and save to reload.
35 |
36 |
37 |
38 |
39 | );
40 | }
41 |
42 | export default Tester;
43 |
--------------------------------------------------------------------------------
/website/src/theme/CodeBlock/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CodeBlock from '@theme-original/CodeBlock';
3 |
4 | export default function CodeBlockWrapper(props) {
5 | return (
6 | <>
7 |
8 | >
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/website/src/theme/Footer/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 | import React from "react";
8 | import {useThemeConfig} from "@docusaurus/theme-common";
9 | import useBaseUrl from "@docusaurus/useBaseUrl";
10 | import styles from "./styles.module.scss";
11 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
12 | import {Helmet} from "react-helmet";
13 |
14 | function Footer() {
15 |
16 | const {footer} = useThemeConfig();
17 | // eslint-disable-next-line no-unused-vars
18 | const {sponsors = []} = useDocusaurusContext();
19 | const {copyright,} = footer || {};
20 |
21 |
22 | if (!footer) {
23 | return null;
24 | }
25 |
26 | const sponsorLogoPath = useBaseUrl("img/sponsors/edouard-montpetit-logo.svg");
27 |
28 | return (
29 |
31 |
32 |
33 |
This project is supported in part by:
34 |
42 |
43 | {copyright ? (
44 |
51 | ) : null}
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | );
60 | }
61 | export default Footer;
62 |
63 |
--------------------------------------------------------------------------------
/website/src/theme/Footer/styles.module.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 | /*
8 | .footerLogoLink {
9 | opacity: 0.5;
10 | transition: opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default);
11 | }
12 |
13 | .footerLogoLink:hover {
14 | opacity: 1;
15 | }
16 | */
17 | .footer .container {
18 | display: flex;
19 | justify-content: space-between;
20 | align-items: center;
21 | }
22 | @media screen and (max-width: 1000px) {
23 | .footer .container {
24 | flex-direction: column;
25 | }
26 | .footer .container .copyright {
27 | margin-top: var(--spacing-md);
28 | }
29 | }
30 | .footer .container p {
31 | margin: 0 var(--spacing-md) 0 0;
32 | }
33 | .footer .container .sponsor {
34 | display: flex;
35 | align-items: center;
36 | }
37 | .footer .container .sponsor .sponsors {
38 | display: flex;
39 | flex-wrap: wrap;
40 | }
41 | .footer .container img {
42 | width: 150px;
43 | height: 50px;
44 | object-fit: fill;
45 | }
46 | html[data-theme=light] .footer .container img {
47 | filter: invert(1);
48 | }
49 |
50 | /*# sourceMappingURL=styles.module.css.map */
51 |
--------------------------------------------------------------------------------
/website/src/theme/Footer/styles.module.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["styles.module.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAaE;EAEE;EACA;EACA;;AAEA;EANF;IAOI;;EACA;IACE;;;AAIJ;EACE;;AAGF;EACE;EACA;;AACA;EACE;EACA;;AAKJ;EACE;EACA;EAEA;;AAGA;EACE","file":"styles.module.css"}
--------------------------------------------------------------------------------
/website/src/theme/Footer/styles.module.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 | /*
8 | .footerLogoLink {
9 | opacity: 0.5;
10 | transition: opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default);
11 | }
12 |
13 | .footerLogoLink:hover {
14 | opacity: 1;
15 | }
16 | */
17 |
18 | .footer {
19 |
20 | .container {
21 |
22 | display: flex;
23 | justify-content: space-between;
24 | align-items: center;
25 |
26 | @media screen and (max-width: 1000px){
27 | flex-direction: column;
28 | .copyright{
29 | margin-top: var(--spacing-md);
30 | }
31 | }
32 |
33 | p{
34 | margin: 0 var(--spacing-md) 0 0;
35 | }
36 |
37 | .sponsor{
38 | display: flex;
39 | align-items: center;
40 | .sponsors{
41 | display: flex;
42 | flex-wrap: wrap;
43 |
44 | }
45 | }
46 |
47 | img {
48 | width: 150px;
49 | height: 50px;
50 |
51 | object-fit: fill;
52 |
53 |
54 | html[data-theme=light] &{
55 | filter: invert(1);
56 | }
57 | }
58 |
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/website/src/theme/Navbar/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Navbar from '@theme-original/Navbar';
3 |
4 | export default function NavbarWrapper(props) {
5 | return (
6 | <>
7 |
8 | >
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/website/src/theme/Navbar/styles.module.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | /*
9 | Hide toggle in small viewports
10 | */
11 | @media (max-width: 996px) {
12 | .toggle {
13 | display: none;
14 | }
15 | }
16 |
17 | .navbarHideable {
18 | transition: transform var(--ifm-transition-fast) ease;
19 | }
20 |
21 | .navbarHidden {
22 | transform: translate3d(0, calc(-100% - 2px), 0);
23 | }
24 |
25 | .navbarSidebarToggle {
26 | margin-right: 1rem;
27 | }
28 |
29 | .navbarSidebarMobile ul li a{
30 | padding: 10px var(--spacing-sm);
31 | }
32 |
--------------------------------------------------------------------------------
/website/static/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/.nojekyll
--------------------------------------------------------------------------------
/website/static/img/blog/2021-12-01/webmidijs-is-out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/blog/2021-12-01/webmidijs-is-out.png
--------------------------------------------------------------------------------
/website/static/img/blog/jean-philippe_cote.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/blog/jean-philippe_cote.jpg
--------------------------------------------------------------------------------
/website/static/img/docusaurus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/docusaurus.png
--------------------------------------------------------------------------------
/website/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/favicon.ico
--------------------------------------------------------------------------------
/website/static/img/og-card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/og-card.png
--------------------------------------------------------------------------------
/website/static/img/person.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/person.png
--------------------------------------------------------------------------------
/website/static/img/sponsors/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/sponsors/user.png
--------------------------------------------------------------------------------
/website/static/img/tutorial/docsVersionDropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/tutorial/docsVersionDropdown.png
--------------------------------------------------------------------------------
/website/static/img/tutorial/localeDropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/tutorial/localeDropdown.png
--------------------------------------------------------------------------------
/website/static/img/webmidijs-logo-color-on-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
10 |
11 |
12 |
14 |
15 |
16 |
18 |
23 |
29 |
31 |
32 |
36 |
37 |
38 |
39 |
41 |
45 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/website/static/img/webmidijs-logo-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
10 |
11 |
12 |
14 |
15 |
16 |
18 |
23 |
29 |
31 |
32 |
36 |
37 |
38 |
39 |
41 |
45 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/website/static/img/webmidijs-logo-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
11 |
12 |
13 |
15 |
16 |
17 |
19 |
23 |
28 |
30 |
31 |
34 |
35 |
36 |
37 |
39 |
42 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/website/static/img/webmidijs-logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/webmidijs-logo-small.png
--------------------------------------------------------------------------------
/website/static/img/webmidijs3-logo-1178x406.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/webmidijs3-logo-1178x406.png
--------------------------------------------------------------------------------
/website/static/img/webmidijs3-logo-1280x640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/webmidijs3-logo-1280x640.png
--------------------------------------------------------------------------------
/website/static/img/webmidijs3-logo-40x40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djipco/webmidi/f812407b67104ec0d641aa6ed838bca1777ab146/website/static/img/webmidijs3-logo-40x40.png
--------------------------------------------------------------------------------
/website/static/js/newsletter-popup.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line max-len
2 | !function(c,h,i,m,p){m=c.createElement(h),p=c.getElementsByTagName(h)[0],m.async=1,m.src=i,p.parentNode.insertBefore(m,p)}(document,"script","https://chimpstatic.com/mcjs-connected/js/users/4ad018201643381a89d30000c/0e8ce2085fb209fed1b656a74.js");
3 |
--------------------------------------------------------------------------------
/website/static/styles/default.css:
--------------------------------------------------------------------------------
1 | a {
2 | text-decoration: none;
3 | }
4 | a:link {
5 | color: #ffd000;
6 | }
7 | a:visited {
8 | color: #ffd000;
9 | }
10 | a:hover {
11 | color: #ffd000;
12 | text-decoration: underline;
13 | }
14 | a:active {
15 | color: #ffd000;
16 | }
17 |
18 | body {
19 | font-family: "Yanone Kaffeesatz", sans-serif;
20 | font-size: 24px;
21 | line-height: 1.25;
22 | text-align: justify;
23 | }
24 |
25 | header {
26 | margin: 5vh auto;
27 | width: 75%;
28 | max-width: 500px;
29 | }
30 | header img {
31 | max-width: 100%;
32 | }
33 |
34 | main {
35 | margin: 0 auto;
36 | width: 75%;
37 | max-width: 500px;
38 | padding: 0 50px;
39 | }
40 |
41 | /*# sourceMappingURL=default.css.map */
42 |
--------------------------------------------------------------------------------
/website/static/styles/default.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["default.scss"],"names":[],"mappings":"AAGA;EAEE;;AAEA;EACE,OAPS;;AAUX;EACE,OAXS;;AAcX;EACE,OAfS;EAgBT;;AAGF;EACE,OApBS;;;AAyBb;EACE;EACA;EACA;EACA;;;AAGF;EAEE;EACA;EACA;;AAEA;EACE;;;AAKJ;EACE;EACA;EACA;EACA","file":"default.css"}
--------------------------------------------------------------------------------
/website/static/styles/default.scss:
--------------------------------------------------------------------------------
1 | // Colors
2 | $base-color: #ffd000;
3 |
4 | a {
5 |
6 | text-decoration: none;
7 |
8 | &:link {
9 | color: $base-color;
10 | }
11 |
12 | &:visited {
13 | color: $base-color;
14 | }
15 |
16 | &:hover {
17 | color: $base-color;
18 | text-decoration: underline;
19 | }
20 |
21 | &:active {
22 | color: $base-color;
23 | }
24 |
25 | }
26 |
27 | body {
28 | font-family: 'Yanone Kaffeesatz', sans-serif;
29 | font-size: 24px;
30 | line-height: 1.25;
31 | text-align: justify;
32 | }
33 |
34 | header {
35 |
36 | margin: 5vh auto;
37 | width: 75%;
38 | max-width: 500px;
39 |
40 | img {
41 | max-width: 100%;
42 | }
43 |
44 | }
45 |
46 | main {
47 | margin: 0 auto;
48 | width: 75%;
49 | max-width: 500px;
50 | padding: 0 50px;
51 | }
52 |
--------------------------------------------------------------------------------
/website/static/styles/jsdoc.css:
--------------------------------------------------------------------------------
1 | .page-header, pre.code-toolbar > .toolbar:hover {
2 | background-color: red;
3 | }
4 |
--------------------------------------------------------------------------------