Simple monitor for displaying MIDI status for digital pianos.
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

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