├── .gitignore ├── .gitattributes ├── .npmignore ├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── test.js ├── sample-midi-piano ├── screenshot.png ├── preload.js ├── README.md ├── package.json ├── index.js └── index.html ├── sample-midi-player ├── screenshot.png ├── preload.js ├── README.md ├── package.json ├── index.html └── index.js ├── preload.js ├── eslint.config.mjs ├── package.json ├── LICENSE.md ├── test ├── wv.js └── be.js ├── README.md └── jazz-midi-electron.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | sample-* 3 | test* 4 | coverage 5 | eslint.config.mjs 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: jazz-soft 2 | custom: https://paypal.me/jazzsoft 3 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | require('../jazz-midi-electron')().then(function() { console.log('done!'); }, function(err) { console.log(err); }); -------------------------------------------------------------------------------- /sample-midi-piano/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazz-soft/jazz-midi-electron/HEAD/sample-midi-piano/screenshot.png -------------------------------------------------------------------------------- /sample-midi-player/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazz-soft/jazz-midi-electron/HEAD/sample-midi-player/screenshot.png -------------------------------------------------------------------------------- /preload.js: -------------------------------------------------------------------------------- 1 | const { contextBridge, ipcRenderer } = require('electron'); 2 | 3 | contextBridge.exposeInMainWorld('jazz-midi', { 4 | send: (data) => ipcRenderer.send('jazz-midi', data), 5 | receive: (cb) => ipcRenderer.on('jazz-midi', cb) 6 | }); 7 | -------------------------------------------------------------------------------- /sample-midi-player/preload.js: -------------------------------------------------------------------------------- 1 | const { contextBridge, ipcRenderer } = require('electron'); 2 | 3 | contextBridge.exposeInMainWorld('jazz-midi', { 4 | send: (data) => ipcRenderer.send('jazz-midi', data), 5 | receive: (cb) => ipcRenderer.on('jazz-midi', cb) 6 | }); 7 | contextBridge.exposeInMainWorld('onLoadMidi', (cb) => ipcRenderer.on('load-midi', cb)); 8 | -------------------------------------------------------------------------------- /sample-midi-piano/preload.js: -------------------------------------------------------------------------------- 1 | const { contextBridge, ipcRenderer } = require('electron'); 2 | 3 | contextBridge.exposeInMainWorld('jazz-midi', { 4 | send: (data) => ipcRenderer.send('jazz-midi', data), 5 | receive: (cb) => ipcRenderer.on('jazz-midi', cb) 6 | }); 7 | contextBridge.exposeInMainWorld('electronOpenUrl', (url) => ipcRenderer.send('open-url', url)); 8 | -------------------------------------------------------------------------------- /sample-midi-piano/README.md: -------------------------------------------------------------------------------- 1 | # MIDI Piano (Electron) 2 | 3 | [![screenshot](screenshot.png)](https://github.com/jazz-soft/jazz-midi-electron/tree/master/sample-midi-piano) 4 | 5 | ## Install 6 | `npm install` 7 | 8 | ## Run 9 | `electron .` 10 | or 11 | `npm start` 12 | 13 | ## Package 14 | `npm run make` 15 | 16 | *Back to [**jazz-midi-electron**](https://github.com/jazz-soft/jazz-midi-electron).* 17 | -------------------------------------------------------------------------------- /sample-midi-player/README.md: -------------------------------------------------------------------------------- 1 | # MIDI Player (Electron) 2 | 3 | [![screenshot](screenshot.png)](https://github.com/jazz-soft/jazz-midi-electron/tree/master/sample-midi-player) 4 | 5 | ## Install 6 | `npm install` 7 | 8 | ## Run 9 | `electron .` 10 | or 11 | `npm start` 12 | 13 | ## Package 14 | `npm run make` 15 | 16 | *Back to [**jazz-midi-electron**](https://github.com/jazz-soft/jazz-midi-electron).* 17 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | - uses: actions/setup-node@v4 9 | with: 10 | node-version: '20.x' 11 | - run: npm install 12 | - run: npm test 13 | - run: npm run coverage 14 | - uses: coverallsapp/github-action@v2 15 | with: 16 | github-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import js from "@eslint/js"; 3 | 4 | export default [ 5 | js.configs.recommended, 6 | { 7 | files: ["**/*.js"], 8 | languageOptions: { 9 | ecmaVersion: 2015, 10 | globals: { 11 | ...globals.browser, 12 | ...globals.node, 13 | "ipcMainTestFake": "readonly", 14 | "webContentsTestFake": "readonly", 15 | "define": "readonly" 16 | } 17 | }, 18 | rules: { 19 | "no-inner-declarations" : "off", 20 | "no-empty" : ["warn", { "allowEmptyCatch": true }] 21 | } 22 | } 23 | ]; -------------------------------------------------------------------------------- /sample-midi-piano/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample-midi-piano", 3 | "version": "0.0.0", 4 | "description": "Sample JZZ MIDI project for Electron", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "electron-forge start", 8 | "package": "electron-forge package", 9 | "make": "electron-forge make" 10 | }, 11 | "author": "jazz-soft (https://jazz-soft.net/)", 12 | "dependencies": { 13 | "electron-squirrel-startup": "^1.0.1", 14 | "jazz-midi-electron": "^2.0.3", 15 | "jzz": "^1.9.2", 16 | "jzz-gui-select": "^1.1.7", 17 | "jzz-input-kbd": "^1.3.4", 18 | "jzz-synth-tiny": "^1.4.3" 19 | }, 20 | "devDependencies": { 21 | "@electron-forge/cli": "^7.8.0", 22 | "@electron-forge/maker-deb": "^7.8.0", 23 | "@electron-forge/maker-rpm": "^7.8.0", 24 | "@electron-forge/maker-squirrel": "^7.8.0", 25 | "@electron-forge/maker-zip": "^7.8.0", 26 | "electron": "^35.1.4" 27 | }, 28 | "license": "MIT", 29 | "private": true 30 | } 31 | -------------------------------------------------------------------------------- /sample-midi-player/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample-midi-player", 3 | "version": "0.0.0", 4 | "description": "Sample MIDI Player project for Electron", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "electron-forge start", 8 | "package": "electron-forge package", 9 | "make": "electron-forge make" 10 | }, 11 | "author": "jazz-soft (https://jazz-soft.net/)", 12 | "dependencies": { 13 | "electron-squirrel-startup": "^1.0.1", 14 | "jazz-midi-electron": "^2.0.3", 15 | "jzz": "^1.9.2", 16 | "jzz-gui-player": "^1.7.7", 17 | "jzz-midi-smf": "^1.9.7", 18 | "jzz-synth-tiny": "^1.4.3" 19 | }, 20 | "devDependencies": { 21 | "@electron-forge/cli": "^7.8.0", 22 | "@electron-forge/maker-deb": "^7.8.0", 23 | "@electron-forge/maker-rpm": "^7.8.0", 24 | "@electron-forge/maker-squirrel": "^7.8.0", 25 | "@electron-forge/maker-zip": "^7.8.0", 26 | "electron": "^35.1.4" 27 | }, 28 | "license": "MIT", 29 | "private": true 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jazz-midi-electron", 3 | "version": "2.0.3", 4 | "description": "MIDI for Electron", 5 | "main": "jazz-midi-electron.js", 6 | "scripts": { 7 | "test": "nyc mocha test/wv.js && nyc --no-clean mocha test/be.js", 8 | "lint": "eslint jazz-midi-electron.js", 9 | "coverage": "nyc report --reporter=lcov" 10 | }, 11 | "keywords": [ 12 | "midi", 13 | "midi2", 14 | "webmidi", 15 | "webmidiapi", 16 | "web-midi", 17 | "web-midi-api", 18 | "electron" 19 | ], 20 | "author": "jazz-soft (http://jazz-soft.net/)", 21 | "dependencies": { 22 | "jzz": "^1.9.2" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/jazz-soft/jazz-midi-electron.git" 27 | }, 28 | "homepage": "https://jazz-soft.net", 29 | "bugs": "https://github.com/jazz-soft/jazz-midi-electron/issues", 30 | "license": "MIT", 31 | "devDependencies": { 32 | "electron": "^35.1.4", 33 | "eslint": "^9.24.0", 34 | "mocha": "^11.1.0", 35 | "nyc": "^17.1.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-25 Jazz-Soft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/wv.js: -------------------------------------------------------------------------------- 1 | webview(); // WebView environment 2 | const assert = require('assert'); 3 | const version = require('../package.json').version; 4 | const JME = require('..'); 5 | 6 | describe('webview', function() { 7 | it('version ' + version, function() { 8 | assert.equal(JME.version(), version); 9 | }); 10 | it('context: webview', function() { 11 | assert.equal(JME.context(), 'webview'); 12 | }); 13 | it('send', function() { 14 | document.dispatchEvent(new CustomEvent('jazz-midi')); 15 | }); 16 | it('receive', function() { 17 | window.dispatchEvent({ type: 'message', data: { type: 'jazz-midi-msg' } }); 18 | }); 19 | }); 20 | 21 | function DOC() {} 22 | function WIN() { this['jazz-midi'] = this; } 23 | function CustomEvent(t, d) { 24 | this.type = t; 25 | this.detail = d; 26 | } 27 | function webview() { 28 | DOC.prototype.addEventListener = function(t, f) { 29 | this.listener = f; 30 | }; 31 | DOC.prototype.dispatchEvent = function(e) { this.listener(e); }; 32 | 33 | WIN.prototype.send = function(f) {}; 34 | WIN.prototype.receive = function(f) { this.listener = f; }; 35 | WIN.prototype.dispatchEvent = function(e) { this.listener(e); }; 36 | 37 | global.document = new DOC(); 38 | global.window = new WIN(); 39 | global.CustomEvent = CustomEvent; 40 | global.navigator = true; 41 | } 42 | -------------------------------------------------------------------------------- /sample-midi-player/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | sample-midi-player 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 |

MIDI Player

18 |
19 |

20 | 
21 | 
22 | 
46 | 
47 | 


--------------------------------------------------------------------------------
/sample-midi-piano/index.js:
--------------------------------------------------------------------------------
 1 | const { app, BrowserWindow, ipcMain, Menu, shell } = require('electron');
 2 | const path = require('path');
 3 | const url = require('url');
 4 | const JZZ = require('jzz');
 5 | const JME = require('jazz-midi-electron');
 6 | 
 7 | // optional: adding virtual MIDI-Out port (output to the console)
 8 | var vmOut = JZZ.Widget();
 9 | vmOut.connect(function(msg) {
10 |   console.log(msg.toString());
11 | });
12 | JZZ.addMidiOut('Virtual MIDI-Out', vmOut);
13 | 
14 | // optional: adding virtual MIDI-In port (generate random notes)
15 | var vmIn = JZZ.Widget();
16 | var n;
17 | setInterval(function() {
18 |     if (n) {
19 |         vmIn.noteOff(0, n);
20 |         n = undefined;
21 |     }
22 |     else {
23 |         n = Math.floor(Math.random() * 12) + 60;
24 |         vmIn.noteOn(0, n, 127);
25 |     }
26 | }, 500);
27 | JZZ.addMidiIn('Virtual MIDI-In', vmIn);
28 | 
29 | let win;
30 | const isMac = process.platform === 'darwin';
31 | 
32 | function createWindow () {
33 |   win = new BrowserWindow({
34 |     width: 640,
35 |     height: 400,
36 |     webPreferences: {
37 |       preload: path.join(__dirname, 'preload.js')
38 |     }
39 |   });
40 |   //uncomment for debug:
41 |   //win.webContents.openDevTools();
42 | 
43 |   //enable MIDI in the renderer view:
44 |   JME.init(win);
45 | 
46 |   win.loadURL(url.format({
47 |     pathname: path.join(__dirname, 'index.html'),
48 |     protocol: 'file:',
49 |     slashes: true
50 |   }));
51 | }
52 | 
53 | ipcMain.on('open-url', (evt, url) => { shell.openExternal(url); });
54 | 
55 | Menu.setApplicationMenu(null);
56 | 
57 | app.on('ready', createWindow);
58 | 
59 | app.on('window-all-closed', () => { if (!isMac) app.quit(); });
60 | 
61 | app.on('activate', () => { if (win === null) createWindow(); });
62 | 


--------------------------------------------------------------------------------
/sample-midi-player/index.js:
--------------------------------------------------------------------------------
 1 | const { app, BrowserWindow, Menu, dialog } = require('electron');
 2 | const path = require('path');
 3 | const fs = require('fs');
 4 | const url = require('url');
 5 | const JZZ = require('jzz');
 6 | const JME = require('jazz-midi-electron');
 7 | 
 8 | // optional: adding virtual MIDI-Out port (output to the console)
 9 | var vmOut = JZZ.Widget();
10 | vmOut.connect(function(msg) {
11 |   console.log(msg.toString());
12 | });
13 | JZZ.addMidiOut('Virtual MIDI-Out', vmOut);
14 | 
15 | let win;
16 | const isMac = process.platform === 'darwin';
17 | 
18 | function createWindow () {
19 |   win = new BrowserWindow({
20 |     width: 640,
21 |     height: 400,
22 |     webPreferences: {
23 |       preload: path.join(__dirname, 'preload.js')
24 |     }
25 |   });
26 |   //uncomment for debug:
27 |   //win.webContents.openDevTools();
28 | 
29 |   //enable MIDI in the renderer view:
30 |   JME.init(win);
31 | 
32 |   win.loadURL(url.format({
33 |     pathname: path.join(__dirname, 'index.html'),
34 |     protocol: 'file:',
35 |     slashes: true
36 |   }));
37 | }
38 | 
39 | function openFile() {
40 |   dialog.showOpenDialog({
41 |     properties: ['openFile'],
42 |     filters: [{ name: 'MIDI Files', extensions: ['mid', 'midi', 'midi2', 'kar', 'rmi'] }, { name: 'All Files', extensions: ['*'] }]
43 |   }).then(function(result) {
44 |     if (result.filePaths && result.filePaths[0]) {
45 |       fs.readFile(result.filePaths[0], 'binary', (err, data) => {
46 |         win.webContents.send('load-midi', err ?
47 |           { err: 'Cannot open ' + result.filePaths[0] + ': ' + err } :
48 |           { file: result.filePaths[0], data: data }
49 |         );
50 |       });
51 |     }
52 |   });
53 | }
54 | 
55 | const menu = Menu.buildFromTemplate([
56 |   ...(isMac ? [{
57 |     label: app.name,
58 |     submenu: [
59 |       { role: 'about' },
60 |       { type: 'separator' },
61 |       { role: 'services' },
62 |       { type: 'separator' },
63 |       { role: 'hide' },
64 |       { role: 'hideOthers' },
65 |       { role: 'unhide' },
66 |       { type: 'separator' },
67 |       { role: 'quit' }
68 |     ]
69 |   }] : []),
70 |   {
71 |     label: 'File',
72 |     submenu: [
73 |       { label: 'Open', click: openFile },
74 |       isMac ? { role: 'close' } : { role: 'quit' }
75 |     ]
76 |   }
77 | ]);
78 | Menu.setApplicationMenu(menu);
79 | 
80 | app.on('ready', createWindow);
81 | 
82 | app.on('window-all-closed', () => { if (!isMac) app.quit(); });
83 | 
84 | app.on('activate', () => { if (win === null) createWindow(); });
85 | 


--------------------------------------------------------------------------------
/sample-midi-piano/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 5 | sample-midi-piano
 6 | 
 7 | 
 8 | 
 9 | 
10 | 
11 | 
16 | 
17 | 
18 | 

Sample JZZ MIDI project

19 | 20 |

21 | MIDI In: ⟹ 22 | MIDI Out: 23 |

24 | 25 |

26 | 27 |

28 | https://jazz-soft.net 29 |

30 | 31 | 32 | 65 | 66 | -------------------------------------------------------------------------------- /test/be.js: -------------------------------------------------------------------------------- 1 | backend(); // Electron Back End environment 2 | const assert = require('assert'); 3 | const JME = require('..'); 4 | var ipc, wc1, wc2, win2, win1, fake_in; 5 | 6 | describe('backend', function() { 7 | it('context: backend', function() { 8 | assert.equal(JME.context(), 'backend'); 9 | }); 10 | it('init', function() { 11 | JME.init(win1); 12 | JME.init(win2); 13 | win1.send(); 14 | }); 15 | it('new', function() { 16 | win1.send(['new']); 17 | }); 18 | it('refresh', function() { 19 | win1.send(['refresh']); 20 | }); 21 | it('openout', function() { 22 | win1.send(['openout', 0, 'Not existent']); 23 | win1.send(['openout', 0, 'Fake MIDI-Out 1']); 24 | win1.send(['openout', 0, 'Fake MIDI-Out 1']); 25 | win1.send(['openout', 0, 'Fake MIDI-Out 2']); 26 | win1.send(['openout', 1, 'Fake MIDI-Out 1']); 27 | }); 28 | it('openin', function() { 29 | win1.send(['openin', 0, 'Not existent']); 30 | win1.send(['openin', 0, 'Fake MIDI-In 1']); 31 | win1.send(['openin', 0, 'Fake MIDI-In 1']); 32 | win1.send(['openin', 0, 'Fake MIDI-In 2']); 33 | win1.send(['openin', 1, 'Fake MIDI-In 1']); 34 | }); 35 | it('closeout', function() { 36 | win1.send(['closeout', 0]); 37 | win1.send(['closeout', 0]); 38 | }); 39 | it('closein', function() { 40 | win1.send(['closein', 0]); 41 | win1.send(['closein', 0]); 42 | }); 43 | it('midi', function() { 44 | fake_in._emit([]); 45 | fake_in._emit([0x90, 0x60, 0x7f]); 46 | }); 47 | it('play', function() { 48 | win1.send(['play', 0, 0x90, 0x60, 0x7f]); 49 | win1.send(['play', 1, 0x90, 0x60, 0x7f]); 50 | }); 51 | it('other', function() { 52 | win1.send(['other']); 53 | }); 54 | it('exit', function() { 55 | win1.close(); 56 | }); 57 | }); 58 | 59 | function WC() {} 60 | function WIN(wc) { this.webContents = wc; this.handles = {} } 61 | function IpcMain() { this.handles = {} } 62 | function backend() { 63 | WC.prototype.send = function() {} 64 | WIN.prototype.on = function(evt, fn) { 65 | this.handles[evt] = fn; 66 | } 67 | WIN.prototype.send = function(data) { 68 | if (ipc.handles['jazz-midi']) ipc.handles['jazz-midi']({ sender: { id: this.webContents } }, data); 69 | } 70 | WIN.prototype.close = function() { 71 | if (this.handles.closed) this.handles.closed(); 72 | } 73 | IpcMain.prototype.on = function(evt, fn) { 74 | this.handles[evt] = fn; 75 | } 76 | wc1 = new WC(); 77 | wc2 = new WC(); 78 | win1 = new WIN(wc1); 79 | win2 = new WIN(wc2); 80 | ipc = new IpcMain(); 81 | global.ipcMainTestFake = ipc; 82 | global.webContentsTestFake = { fromId: function(x) { return x; } }; 83 | const JZZ = require('jzz'); 84 | fake_in = JZZ.Widget(); 85 | JZZ.addMidiIn('Fake MIDI-In 1', fake_in); 86 | JZZ.addMidiIn('Fake MIDI-In 2', JZZ.Widget()); 87 | JZZ.addMidiOut('Fake MIDI-Out 1', JZZ.Widget()); 88 | JZZ.addMidiOut('Fake MIDI-Out 2', JZZ.Widget()); 89 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jazz-midi-electron 2 | 3 | [![npm](https://img.shields.io/npm/v/jazz-midi-electron.svg)](https://www.npmjs.com/package/jazz-midi-electron) 4 | [![npm](https://img.shields.io/npm/dt/jazz-midi-electron.svg)](https://www.npmjs.com/package/jazz-midi-electron) 5 | [![build](https://github.com/jazz-soft/jazz-midi-electron/actions/workflows/build.yml/badge.svg)](https://github.com/jazz-soft/jazz-midi-electron/actions) 6 | [![Coverage Status](https://coveralls.io/repos/github/jazz-soft/jazz-midi-electron/badge.svg?branch=master)](https://coveralls.io/github/jazz-soft/jazz-midi-electron?branch=master) 7 | 8 | ## MIDI for Electron 9 | 10 | MIDI integration in [**Electron**](https://electronjs.org) applications. 11 | 12 | ( see also: [jazz-midi-vscode](https://github.com/jazz-soft/jazz-midi-vscode) ... ) 13 | 14 | *Notice:* v2.x.x introduces some breaking changes, 15 | however, upgrading old projects from v1.x.x will well worth the trouble. 16 | You are getting: 17 | - fewer dependencies 18 | - improved code security 19 | - no additional installs required 20 | 21 | ## Examples 22 | 23 | [**sample-midi-piano**](https://github.com/jazz-soft/jazz-midi-electron/tree/master/sample-midi-piano) 24 | [![screenshot](https://raw.githubusercontent.com/jazz-soft/jazz-midi-electron/master/sample-midi-piano/screenshot.png)](https://github.com/jazz-soft/jazz-midi-electron/tree/master/sample-midi-piano) 25 | 26 | [**sample-midi-player**](https://github.com/jazz-soft/jazz-midi-electron/tree/master/sample-midi-piano) 27 | [![screenshot](https://raw.githubusercontent.com/jazz-soft/jazz-midi-electron/master/sample-midi-player/screenshot.png)](https://github.com/jazz-soft/jazz-midi-electron/tree/master/sample-midi-player) 28 | 29 | ## Install 30 | `npm install jazz-midi-electron --save` 31 | 32 | ## Usage 33 | ( see the examples above... ) 34 | 35 | ### Main Process 36 | 37 | ```js 38 | const JZZ = require('jzz'); 39 | // jazz-midi-electron is not required if using MIDI only in the Main Process 40 | // ... 41 | JZZ().openMidiOut() // ... 42 | ``` 43 | Main Process can use [**JZZ.js**](https://github.com/jazz-soft/JZZ) as normal Node.js application. 44 | It can access regular MIDI ports and create virtual ports. 45 | 46 | ### Browser Window 47 | 48 | ```html 49 | 50 | 51 | // ... 52 | JZZ().openMidiOut() // ... 53 | ``` 54 | ```js 55 | // when creating the Browser Window: 56 | const JME = require('jazz-midi-electron'); 57 | // ... 58 | win = new BrowserWindow({ 59 | // ... 60 | webPreferences: { // see the preload.js in this repository 61 | preload: path.join(__dirname, 'preload.js') 62 | } 63 | }); 64 | JME.init(win); 65 | // ... 66 | ``` 67 | Browser Window will see all MIDI ports (including virtual) available to the Main Process. 68 | It can create additional Web Audio and HTML-based MIDI ports 69 | (see [jzz-synth-tiny](https://github.com/jazz-soft/JZZ-synth-Tiny) and [jzz-input-kbd](https://github.com/jazz-soft/JZZ-input-Kbd)). 70 | -------------------------------------------------------------------------------- /jazz-midi-electron.js: -------------------------------------------------------------------------------- 1 | (function(global, factory) { 2 | /* istanbul ignore next */ 3 | if (typeof exports === 'object' && typeof module !== 'undefined') { 4 | module.exports = factory(); 5 | } 6 | else if (typeof define === 'function' && define.amd) { 7 | define('JME', [], factory); 8 | } 9 | else { 10 | if (!global) global = window; 11 | if (global.JME) return; 12 | global.JME = factory(); 13 | } 14 | })(this, function() { 15 | 16 | var _ver = '2.0.3'; 17 | var _env = 'backend'; 18 | var JME = { 19 | version: function() { return _ver; }, 20 | context: function() { return _env; } 21 | }; 22 | if (typeof navigator != 'undefined') { 23 | _env = 'webview'; 24 | document.addEventListener('jazz-midi', function(msg) { 25 | window['jazz-midi'].send(msg.detail); 26 | }); 27 | window['jazz-midi'].receive(function(evt, data) { 28 | document.dispatchEvent(new CustomEvent('jazz-midi-msg', { detail: data })); 29 | }); 30 | } 31 | else { 32 | var JZZ = require('jzz'); 33 | var electron = require('electron'); 34 | var ipcMain = electron.ipcMain || ipcMainTestFake; 35 | var webContents = electron.webContents || webContentsTestFake; 36 | var CLs = []; 37 | var client = function(wc, n) { 38 | var c; 39 | for (c of CLs) if (c.wc == wc && c.n == n) return c; 40 | c = { wc: wc, n: n }; 41 | CLs.push(c); 42 | return c; 43 | } 44 | ipcMain.on('jazz-midi', function(evt, data) { 45 | var i, c, p, s; 46 | var wc = webContents.fromId(evt.sender.id); 47 | if (!data || data[0] == 'version') { 48 | wc.send('jazz-midi', ['version', 0, _ver]); 49 | } 50 | else if (data[0] == 'new') { 51 | i = 0; 52 | for (c of CLs) if (c.wc == wc) i++; 53 | wc.send('jazz-midi', ['version', i, _ver]); 54 | } 55 | else if (data[0] == 'refresh') { 56 | JZZ().refresh().and(function() { 57 | var info = this.info(); 58 | var ins = []; 59 | var outs = []; 60 | for (i = 0; i < info.inputs.length; i++) { 61 | ins.push({ name: info.inputs[i].name, manufacturer: info.inputs[i].manufacturer, version: info.inputs[i].version }); 62 | } 63 | for (i = 0; i < info.outputs.length; i++) { 64 | outs.push({ name: info.outputs[i].name, manufacturer: info.outputs[i].manufacturer, version: info.outputs[i].version }); 65 | } 66 | wc.send('jazz-midi', ['refresh', { ins: ins, outs: outs }]); 67 | }); 68 | } 69 | else if (data[0] == 'openout') { 70 | c = client(wc, data[1]); 71 | p = c.out; 72 | s = p ? p.name() : ''; 73 | if (s == data[2]) { 74 | wc.send('jazz-midi', ['openout', data[1], data[2]]); 75 | return; 76 | } 77 | JZZ().openMidiOut(data[2]).then(function() { 78 | c.out = this; 79 | if (p) p.close(); 80 | wc.send('jazz-midi', ['openout', data[1], data[2]]); 81 | }, function() { 82 | wc.send('jazz-midi', ['openout', data[1], s]); 83 | }); 84 | } 85 | else if (data[0] == 'openin') { 86 | c = client(wc, data[1]); 87 | p = c.in; 88 | s = p ? p.name() : ''; 89 | if (s == data[2]) { 90 | wc.send('jazz-midi', ['openin', data[1], data[2]]); 91 | return; 92 | } 93 | JZZ().openMidiIn(data[2]).then(function() { 94 | c.in = this; 95 | if (p) p.close(); 96 | wc.send('jazz-midi', ['openin', data[1], data[2]]); 97 | c.in.connect(function(midi) { 98 | if (midi.length) wc.send('jazz-midi', ['midi', data[1], 0].concat(midi.slice())); 99 | }); 100 | }, function() { 101 | wc.send('jazz-midi', ['openin', data[1], s]); 102 | }); 103 | } 104 | else if (data[0] == 'closeout') { 105 | c = client(wc, data[1]); 106 | if (c.out) c.out.close(); 107 | delete c.out; 108 | } 109 | else if (data[0] == 'closein') { 110 | c = client(wc, data[1]); 111 | if (c.in) c.in.close(); 112 | delete c.in; 113 | } 114 | else if (data[0] == 'play') { 115 | c = client(wc, data[1]); 116 | if (c.out) c.out.send(data.slice(2)); 117 | } 118 | }); 119 | JME.init = function(win) { 120 | var wc = win.webContents; 121 | client(wc, 0); 122 | win.on('closed', function() { 123 | var CC = []; 124 | var c; 125 | for (c of CLs) { 126 | if (c.wc == wc) { 127 | if (c.out) c.out.close(); 128 | if (c.in) c.in.close(); 129 | } 130 | else { 131 | CC.push(c); 132 | } 133 | } 134 | CLs = CC; 135 | }) 136 | }; 137 | } 138 | return JME; 139 | }); 140 | --------------------------------------------------------------------------------