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.
 
 
 
 

160 lignes
4.1 KiB

  1. import { app, shell, BrowserWindow, ipcMain } from 'electron'
  2. import { join } from 'path'
  3. import { readFile, stat, writeFile } from "fs/promises";
  4. import { electronApp, optimizer, is } from '@electron-toolkit/utils'
  5. import icon from '../../resources/icon.png?asset'
  6. interface Config {
  7. range: string
  8. queryDeviceKey: string
  9. scaleFactor: string
  10. }
  11. const defaultConfig: Config = {
  12. range: '21|108',
  13. queryDeviceKey: '',
  14. scaleFactor: '1'
  15. }
  16. const configPath = './config.json'
  17. const naturalKeyWidth = 20
  18. const pedalBoardWidth = 239
  19. const height = 200
  20. const getNaturalKeys = (range: string): number => {
  21. const [startKeyRaw, endKeyRaw] = range.split('|')
  22. const startKey = Number(startKeyRaw)
  23. const endKey = Number(endKeyRaw)
  24. let naturalKeys = 0
  25. for (let i = startKey; i <= endKey; i += 1) {
  26. switch (i % 12) {
  27. case 0:
  28. case 2:
  29. case 4:
  30. case 5:
  31. case 7:
  32. case 9:
  33. case 11:
  34. naturalKeys += 1
  35. break
  36. default:
  37. break
  38. }
  39. }
  40. return naturalKeys
  41. }
  42. function createWindow(config: Config): void {
  43. // Create the browser window.
  44. const naturalKeys = getNaturalKeys(config.range)
  45. const width = naturalKeys * naturalKeyWidth + pedalBoardWidth
  46. const mainWindow = new BrowserWindow({
  47. width,
  48. height,
  49. show: false,
  50. autoHideMenuBar: true,
  51. ...(process.platform === 'linux' ? { icon } : {}),
  52. webPreferences: {
  53. preload: join(__dirname, '../preload/index.js'),
  54. sandbox: false,
  55. devTools: is.dev
  56. },
  57. maximizable: is.dev,
  58. minimizable: false,
  59. maxHeight: is.dev ? undefined : height,
  60. minHeight: is.dev ? undefined : height,
  61. minWidth: is.dev ? undefined : width,
  62. maxWidth: is.dev ? undefined : width,
  63. fullscreenable: false,
  64. resizable: is.dev
  65. })
  66. mainWindow.on('ready-to-show', () => {
  67. mainWindow.show()
  68. })
  69. mainWindow.webContents.setWindowOpenHandler((details) => {
  70. void shell.openExternal(details.url)
  71. return { action: 'deny' }
  72. })
  73. let search: URLSearchParams
  74. if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
  75. const url = new URL(process.env['ELECTRON_RENDERER_URL'])
  76. search = new URLSearchParams(url.searchParams)
  77. Object.entries(config).forEach(([key, value]) => {
  78. search.set(key, value)
  79. })
  80. url.search = search.toString()
  81. void mainWindow.loadURL(url.toString())
  82. return
  83. }
  84. search = new URLSearchParams()
  85. Object.entries(config).forEach(([key, value]) => {
  86. search.set(key, value)
  87. })
  88. void mainWindow.loadFile(join(__dirname, '../renderer/index.html'), {
  89. search: search.toString()
  90. })
  91. }
  92. app.whenReady().then(async () => {
  93. const effectiveConfig = { ...defaultConfig }
  94. try {
  95. const theStat = await stat(configPath)
  96. if (theStat.isDirectory()) {
  97. return
  98. }
  99. const jsonRaw = await readFile(configPath, 'utf-8')
  100. const json = JSON.parse(jsonRaw)
  101. Object.entries(json).forEach(([key, value]) => {
  102. effectiveConfig[key] = value
  103. })
  104. } catch {
  105. await writeFile(configPath, JSON.stringify(defaultConfig))
  106. }
  107. electronApp.setAppUserModelId('sh.modal.pianomidimonitor')
  108. app.on('browser-window-created', (_, window) => {
  109. optimizer.watchWindowShortcuts(window)
  110. })
  111. ipcMain.on('querydevicekeychange', async (_event, value) => {
  112. effectiveConfig.queryDeviceKey = value
  113. await writeFile(configPath, JSON.stringify(effectiveConfig))
  114. })
  115. ipcMain.on('scalefactorchange', async (_event, value) => {
  116. effectiveConfig.scaleFactor = value
  117. await writeFile(configPath, JSON.stringify(effectiveConfig))
  118. })
  119. ipcMain.on('rangechange', async (event, value) => {
  120. effectiveConfig.range = value
  121. await writeFile(configPath, JSON.stringify(effectiveConfig))
  122. const webContents = event.sender
  123. const win = BrowserWindow.fromWebContents(webContents)
  124. if (!win) {
  125. return
  126. }
  127. win.close()
  128. createWindow(effectiveConfig)
  129. })
  130. createWindow(effectiveConfig)
  131. app.on('activate', function () {
  132. if (BrowserWindow.getAllWindows().length === 0) {
  133. createWindow(effectiveConfig)
  134. }
  135. })
  136. })
  137. app.on('window-all-closed', () => {
  138. if (process.platform !== 'darwin') {
  139. app.quit()
  140. }
  141. })