Simple monitor for displaying MIDI status for digital pianos.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

195 lignes
4.9 KiB

  1. import { app, BrowserWindow, Menu, ipcMain, } from 'electron'
  2. import path from 'path'
  3. import fs from 'fs'
  4. import SPANS from './services/spans.json'
  5. import SCALE_FACTORS from './services/scaleFactors.json'
  6. import { _ } from './services/messages'
  7. import getKeyName from './services/getKeyName'
  8. import getNaturalKeyCount from './services/getNaturalKeyCount'
  9. import Config from './services/Config'
  10. // @ts-ignore
  11. import electronIsDev = require('electron-is-dev')
  12. // @ts-ignore
  13. import midi = require('midi')
  14. // @ts-ignore
  15. const WINDOW_HEIGHT = 100
  16. let config: Config = {
  17. startKey: 21,
  18. endKey: 108,
  19. scaleFactor: 1,
  20. }
  21. const createWindow = () => {
  22. const KEYS_WIDTH = getNaturalKeyCount(config.startKey, config.endKey) * 20
  23. const WINDOW_WIDTH = KEYS_WIDTH + 207
  24. const win = new BrowserWindow({
  25. width: WINDOW_WIDTH,
  26. height: WINDOW_HEIGHT,
  27. useContentSize: true,
  28. backgroundColor: '#000000',
  29. resizable: false,
  30. minimizable: false,
  31. maximizable: false,
  32. fullscreenable: false,
  33. frame: false,
  34. webPreferences: {
  35. devTools: electronIsDev,
  36. nodeIntegration: true,
  37. },
  38. })
  39. const baseUrl: string = (
  40. electronIsDev
  41. ? 'http://localhost:3000'
  42. : `file:///${path.join(__dirname, '../build/index.html')}`
  43. )
  44. const search = new URLSearchParams(config as Record<string, string>)
  45. const url = new URL(baseUrl)
  46. url.search = search.toString()
  47. win.loadURL(url.toString())
  48. if (electronIsDev) {
  49. win.webContents.openDevTools()
  50. }
  51. const input = new midi.Input()
  52. if (input.getPortCount() > 0) {
  53. input.openPort(0)
  54. }
  55. input.on('message', (deltaTime: number, message: unknown[]) => {
  56. const [type, data1, data2] = message
  57. switch (type) {
  58. case 144:
  59. win.webContents.send('note', data1 + ':' + data2)
  60. break
  61. case 176:
  62. win.webContents.send('pedal', data1 + ':' + data2)
  63. break
  64. }
  65. })
  66. }
  67. app
  68. .whenReady()
  69. .then(() => {
  70. try {
  71. const configJsonRaw = fs.readFileSync('./config.json').toString('utf-8')
  72. config = JSON.parse(configJsonRaw)
  73. } catch (e) {
  74. config = {
  75. startKey: 21,
  76. endKey: 108,
  77. scaleFactor: 1,
  78. }
  79. }
  80. const platformMenu = Menu.buildFromTemplate([
  81. ...(
  82. process.platform === 'darwin'
  83. ? [
  84. {
  85. label: app.name,
  86. submenu: [
  87. { role: 'about' },
  88. { type: 'separator' },
  89. { role: 'services' },
  90. { type: 'separator' },
  91. { role: 'hide' },
  92. { role: 'hideothers' },
  93. { role: 'unhide' },
  94. { type: 'separator' },
  95. { role: 'quit' }
  96. ],
  97. }
  98. ]
  99. : []
  100. ) as object[],
  101. {
  102. label: _('VIEW'),
  103. submenu: [
  104. {
  105. label: _('SPAN'),
  106. submenu: SPANS.map(s => ({
  107. label: `${getKeyName(s.startKey)}–${getKeyName(s.endKey)}`,
  108. sublabel: `${s.endKey - s.startKey + 1}-key`,
  109. type: 'radio',
  110. checked: config.startKey === s.startKey && config.endKey === s.endKey,
  111. click: () => {
  112. config.startKey = s.startKey
  113. config.endKey = s.endKey
  114. app.relaunch({
  115. args: process.argv.slice(1).concat([
  116. `--startKey=${config.startKey}`,
  117. `--endKey=${config.endKey}`,
  118. `--scaleFactor=${config.scaleFactor}`,
  119. ])
  120. })
  121. app.exit(0)
  122. },
  123. }))
  124. },
  125. {
  126. label: _('DETAIL_SCALE_FACTOR'),
  127. submenu: SCALE_FACTORS.map(s => ({
  128. label: `${s}×`,
  129. type: 'radio',
  130. checked: config.scaleFactor === s,
  131. click: () => {
  132. config.scaleFactor = s
  133. app.relaunch({
  134. args: process.argv.slice(1).concat([
  135. `--startKey=${config.startKey}`,
  136. `--endKey=${config.endKey}`,
  137. `--scaleFactor=${config.scaleFactor}`,
  138. ])
  139. })
  140. app.exit(0)
  141. },
  142. })),
  143. },
  144. ]
  145. },
  146. ...(
  147. electronIsDev
  148. ? [
  149. {
  150. label: _('DEBUG'),
  151. submenu: [
  152. { role: 'forceReload' },
  153. { role: 'toggleDevTools' },
  154. ]
  155. }
  156. ]
  157. : []
  158. ) as object[]
  159. ])
  160. Menu.setApplicationMenu(platformMenu)
  161. createWindow()
  162. ipcMain.on('quit', () => {
  163. app.quit()
  164. })
  165. })
  166. app.on('quit', () => {
  167. fs.writeFileSync('./config.json', JSON.stringify(config))
  168. })
  169. app.on('window-all-closed', () => {
  170. app.quit()
  171. })
  172. app.on('activate', () => {
  173. if (BrowserWindow.getAllWindows().length === 0) {
  174. createWindow()
  175. }
  176. })