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.
 
 
 
 

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