Simple monitor for displaying MIDI status for digital pianos.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 

195 Zeilen
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. })