Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

457 lines
17KB

  1. #!/usr/bin/env python3
  2. # SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
  3. # SPDX-License-Identifier: GPL-2.0-or-later
  4. # ---------------------------------------------------------------------------------------------------------------------
  5. # Imports (Global)
  6. from math import sqrt
  7. from qt_compat import qt_config
  8. if qt_config == 5:
  9. from PyQt5.QtCore import qCritical, Qt, QSize, QLineF, QRectF
  10. from PyQt5.QtGui import QColor, QLinearGradient, QPainter, QPen, QPixmap
  11. from PyQt5.QtWidgets import QWidget
  12. elif qt_config == 6:
  13. from PyQt6.QtCore import qCritical, Qt, QSize, QLineF, QRectF
  14. from PyQt6.QtGui import QColor, QLinearGradient, QPainter, QPen, QPixmap
  15. from PyQt6.QtWidgets import QWidget
  16. # ---------------------------------------------------------------------------------------------------------------------
  17. # Widget Class
  18. class DigitalPeakMeter(QWidget):
  19. # enum Color
  20. COLOR_GREEN = 1
  21. COLOR_BLUE = 2
  22. # enum Orientation
  23. HORIZONTAL = 1
  24. VERTICAL = 2
  25. # enum Style
  26. STYLE_DEFAULT = 1
  27. STYLE_OPENAV = 2
  28. STYLE_RNCBC = 3
  29. STYLE_CALF = 4
  30. # -----------------------------------------------------------------------------------------------------------------
  31. def __init__(self, parent):
  32. QWidget.__init__(self, parent)
  33. self.setAttribute(Qt.WA_OpaquePaintEvent)
  34. # defaults are VERTICAL, COLOR_GREEN, STYLE_DEFAULT
  35. self.fChannelCount = 0
  36. self.fChannelData = []
  37. self.fLastChannelData = []
  38. self.fMeterColor = self.COLOR_GREEN
  39. self.fMeterColorBase = QColor(93, 231, 61)
  40. self.fMeterColorBaseAlt = QColor(15, 110, 15, 100)
  41. self.fMeterLinesEnabled = True
  42. self.fMeterOrientation = self.VERTICAL
  43. self.fMeterStyle = self.STYLE_DEFAULT
  44. self.fMeterBackground = QColor("#070707")
  45. self.fMeterGradient = QLinearGradient(0, 0, 0, 0)
  46. self.fMeterPixmaps = ()
  47. self.fSmoothMultiplier = 2
  48. self.updateGrandient()
  49. # -----------------------------------------------------------------------------------------------------------------
  50. def channelCount(self):
  51. return self.fChannelCount
  52. def setChannelCount(self, count):
  53. if self.fChannelCount == count:
  54. return
  55. if count < 0:
  56. qCritical(f"DigitalPeakMeter::setChannelCount({count}) - channel count must be a positive integer or zero")
  57. return
  58. self.fChannelCount = count
  59. self.fChannelData = []
  60. self.fLastChannelData = []
  61. for _ in range(count):
  62. self.fChannelData.append(0.0)
  63. self.fLastChannelData.append(0.0)
  64. if self.fMeterStyle == self.STYLE_CALF:
  65. if self.fChannelCount > 0:
  66. self.setFixedSize(100, 12*self.fChannelCount)
  67. else:
  68. self.setMinimumSize(0, 0)
  69. self.setMaximumSize(9999, 9999)
  70. # -----------------------------------------------------------------------------------------------------------------
  71. def meterColor(self):
  72. return self.fMeterColor
  73. def setMeterColor(self, color):
  74. if self.fMeterColor == color:
  75. return
  76. if color not in (self.COLOR_GREEN, self.COLOR_BLUE):
  77. qCritical(f"DigitalPeakMeter::setMeterColor({color}) - invalid color")
  78. return
  79. if color == self.COLOR_GREEN:
  80. self.fMeterColorBase = QColor(93, 231, 61)
  81. self.fMeterColorBaseAlt = QColor(15, 110, 15, 100)
  82. elif color == self.COLOR_BLUE:
  83. self.fMeterColorBase = QColor(82, 238, 248)
  84. self.fMeterColorBaseAlt = QColor(15, 15, 110, 100)
  85. self.fMeterColor = color
  86. self.updateGrandient()
  87. # -----------------------------------------------------------------------------------------------------------------
  88. def meterLinesEnabled(self):
  89. return self.fMeterLinesEnabled
  90. def setMeterLinesEnabled(self, yesNo):
  91. if self.fMeterLinesEnabled == yesNo:
  92. return
  93. self.fMeterLinesEnabled = yesNo
  94. # -----------------------------------------------------------------------------------------------------------------
  95. def meterOrientation(self):
  96. return self.fMeterOrientation
  97. def setMeterOrientation(self, orientation):
  98. if self.fMeterOrientation == orientation:
  99. return
  100. if orientation not in (self.HORIZONTAL, self.VERTICAL):
  101. qCritical(f"DigitalPeakMeter::setMeterOrientation({orientation}) - invalid orientation")
  102. return
  103. self.fMeterOrientation = orientation
  104. self.updateGrandient()
  105. # -----------------------------------------------------------------------------------------------------------------
  106. def meterStyle(self):
  107. return self.fMeterStyle
  108. def setMeterStyle(self, style):
  109. if self.fMeterStyle == style:
  110. return
  111. if style not in (self.STYLE_DEFAULT, self.STYLE_OPENAV, self.STYLE_RNCBC, self.STYLE_CALF):
  112. qCritical(f"DigitalPeakMeter::setMeterStyle({style}) - invalid style")
  113. return
  114. if style == self.STYLE_DEFAULT:
  115. self.fMeterBackground = QColor("#070707")
  116. elif style == self.STYLE_OPENAV:
  117. self.fMeterBackground = QColor("#1A1A1A")
  118. elif style == self.STYLE_RNCBC:
  119. self.fMeterBackground = QColor("#070707")
  120. elif style == self.STYLE_CALF:
  121. self.fMeterBackground = QColor("#000")
  122. if style == self.STYLE_CALF:
  123. self.fMeterPixmaps = (QPixmap(":/bitmaps/meter_calf_off.png"), QPixmap(":/bitmaps/meter_calf_on.png"))
  124. if self.fChannelCount > 0:
  125. self.setFixedSize(100, 12*self.fChannelCount)
  126. else:
  127. self.fMeterPixmaps = ()
  128. self.setMinimumSize(0, 0)
  129. self.setMaximumSize(9999, 9999)
  130. self.fMeterStyle = style
  131. self.updateGrandient()
  132. # -----------------------------------------------------------------------------------------------------------------
  133. def smoothMultiplier(self):
  134. return self.fSmoothMultiplier
  135. def setSmoothMultiplier(self, value):
  136. if self.fSmoothMultiplier == value:
  137. return
  138. if not isinstance(value, int):
  139. qCritical("DigitalPeakMeter::setSmoothMultiplier() - value must be an integer")
  140. return
  141. if value < 0:
  142. qCritical(f"DigitalPeakMeter::setSmoothMultiplier({value}) - value must be >= 0")
  143. return
  144. if value > 5:
  145. qCritical(f"DigitalPeakMeter::setSmoothMultiplier({value}) - value must be < 5")
  146. return
  147. self.fSmoothMultiplier = value
  148. # -----------------------------------------------------------------------------------------------------------------
  149. def displayMeter(self, meter, level, forced = False):
  150. if not isinstance(meter, int):
  151. qCritical("DigitalPeakMeter::displayMeter(,) - meter value must be an integer")
  152. return
  153. if not isinstance(level, float):
  154. qCritical(f"DigitalPeakMeter::displayMeter({meter},) - level value must be a float")
  155. return
  156. if meter <= 0 or meter > self.fChannelCount:
  157. qCritical(f"DigitalPeakMeter::displayMeter({meter}, {level}) - invalid meter number")
  158. return
  159. i = meter - 1
  160. if self.fSmoothMultiplier > 0 and not forced:
  161. level = (
  162. (self.fLastChannelData[i] * float(self.fSmoothMultiplier) + level)
  163. / float(self.fSmoothMultiplier + 1)
  164. )
  165. if level < 0.001:
  166. level = 0.0
  167. elif level > 0.999:
  168. level = 1.0
  169. if self.fChannelData[i] != level:
  170. self.fChannelData[i] = level
  171. self.update()
  172. self.fLastChannelData[i] = level
  173. # -----------------------------------------------------------------------------------------------------------------
  174. def updateGrandient(self):
  175. self.fMeterGradient = QLinearGradient(0, 0, 1, 1)
  176. if self.fMeterStyle == self.STYLE_DEFAULT:
  177. if self.fMeterOrientation == self.HORIZONTAL:
  178. self.fMeterGradient.setColorAt(0.0, self.fMeterColorBase)
  179. self.fMeterGradient.setColorAt(0.2, self.fMeterColorBase)
  180. self.fMeterGradient.setColorAt(0.4, self.fMeterColorBase)
  181. self.fMeterGradient.setColorAt(0.6, self.fMeterColorBase)
  182. self.fMeterGradient.setColorAt(0.8, Qt.yellow)
  183. self.fMeterGradient.setColorAt(1.0, Qt.red)
  184. elif self.fMeterOrientation == self.VERTICAL:
  185. self.fMeterGradient.setColorAt(0.0, Qt.red)
  186. self.fMeterGradient.setColorAt(0.2, Qt.yellow)
  187. self.fMeterGradient.setColorAt(0.4, self.fMeterColorBase)
  188. self.fMeterGradient.setColorAt(0.6, self.fMeterColorBase)
  189. self.fMeterGradient.setColorAt(0.8, self.fMeterColorBase)
  190. self.fMeterGradient.setColorAt(1.0, self.fMeterColorBase)
  191. elif self.fMeterStyle == self.STYLE_RNCBC:
  192. if self.fMeterColor == self.COLOR_BLUE:
  193. c1 = QColor(40,160,160)
  194. c2 = QColor(60,220,160)
  195. elif self.fMeterColor == self.COLOR_GREEN:
  196. c1 = QColor( 40,160,40)
  197. c2 = QColor(160,220,20)
  198. if self.fMeterOrientation == self.HORIZONTAL:
  199. self.fMeterGradient.setColorAt(0.0, c1)
  200. self.fMeterGradient.setColorAt(0.2, c1)
  201. self.fMeterGradient.setColorAt(0.6, c2)
  202. self.fMeterGradient.setColorAt(0.7, QColor(220,220, 20))
  203. self.fMeterGradient.setColorAt(0.8, QColor(240,160, 20))
  204. self.fMeterGradient.setColorAt(0.9, QColor(240, 0, 20))
  205. self.fMeterGradient.setColorAt(1.0, QColor(240, 0, 20))
  206. elif self.fMeterOrientation == self.VERTICAL:
  207. self.fMeterGradient.setColorAt(0.0, QColor(240, 0, 20))
  208. self.fMeterGradient.setColorAt(0.1, QColor(240, 0, 20))
  209. self.fMeterGradient.setColorAt(0.2, QColor(240,160, 20))
  210. self.fMeterGradient.setColorAt(0.3, QColor(220,220, 20))
  211. self.fMeterGradient.setColorAt(0.4, c2)
  212. self.fMeterGradient.setColorAt(0.8, c1)
  213. self.fMeterGradient.setColorAt(1.0, c1)
  214. elif self.fMeterStyle in (self.STYLE_OPENAV, self.STYLE_CALF):
  215. self.fMeterGradient.setColorAt(0.0, self.fMeterColorBase)
  216. self.fMeterGradient.setColorAt(1.0, self.fMeterColorBase)
  217. self.updateGrandientFinalStop()
  218. def updateGrandientFinalStop(self):
  219. if self.fMeterOrientation == self.HORIZONTAL:
  220. self.fMeterGradient.setFinalStop(self.width(), 0)
  221. elif self.fMeterOrientation == self.VERTICAL:
  222. self.fMeterGradient.setFinalStop(0, self.height())
  223. # -----------------------------------------------------------------------------------------------------------------
  224. def minimumSizeHint(self):
  225. return QSize(20, 10) if self.fMeterOrientation == self.HORIZONTAL else QSize(10, 20)
  226. def sizeHint(self):
  227. return QSize(self.width(), self.height())
  228. # -----------------------------------------------------------------------------------------------------------------
  229. def drawCalf(self, event):
  230. painter = QPainter(self)
  231. event.accept()
  232. # no channels, draw black
  233. if self.fChannelCount == 0:
  234. painter.setPen(QPen(Qt.black, 2))
  235. painter.setBrush(Qt.black)
  236. painter.drawRect(0, 0, self.width(), self.height())
  237. return
  238. for i in range(self.fChannelCount):
  239. painter.drawPixmap(0, 12*i, self.fMeterPixmaps[0])
  240. meterPos = 4
  241. meterSize = 12
  242. # draw levels
  243. for level in self.fChannelData:
  244. if level != 0.0:
  245. blevel = int(sqrt(level)*26.0)*3
  246. painter.drawPixmap(5, meterPos, blevel, 4, self.fMeterPixmaps[1], 0, 0, blevel, 4)
  247. meterPos += meterSize
  248. def paintEvent(self, event):
  249. if self.fMeterStyle == self.STYLE_CALF:
  250. self.drawCalf(event)
  251. return
  252. painter = QPainter(self)
  253. event.accept()
  254. width = self.width()
  255. height = self.height()
  256. # draw background
  257. painter.setPen(QPen(self.fMeterBackground, 2))
  258. painter.setBrush(self.fMeterBackground)
  259. painter.drawRect(0, 0, width, height)
  260. if self.fChannelCount == 0:
  261. return
  262. meterPad = 0
  263. meterPos = 0
  264. meterSize = (height if self.fMeterOrientation == self.HORIZONTAL else width)/self.fChannelCount
  265. # set pen/brush for levels
  266. if self.fMeterStyle == self.STYLE_OPENAV:
  267. colorTrans = QColor(self.fMeterColorBase)
  268. colorTrans.setAlphaF(0.5)
  269. painter.setBrush(colorTrans)
  270. painter.setPen(QPen(self.fMeterColorBase, 1))
  271. del colorTrans
  272. meterPad += 2
  273. meterSize -= 2
  274. else:
  275. painter.setPen(QPen(self.fMeterBackground, 0))
  276. painter.setBrush(self.fMeterGradient)
  277. # draw levels
  278. for level in self.fChannelData:
  279. if level == 0.0:
  280. pass
  281. elif self.fMeterOrientation == self.HORIZONTAL:
  282. painter.drawRect(QRectF(0, meterPos, sqrt(level) * float(width), meterSize))
  283. elif self.fMeterOrientation == self.VERTICAL:
  284. painter.drawRect(QRectF(meterPos, height - sqrt(level) * float(height), meterSize, height))
  285. meterPos += meterSize+meterPad
  286. if not self.fMeterLinesEnabled:
  287. return
  288. # draw lines
  289. if self.fMeterOrientation == self.HORIZONTAL:
  290. # Variables
  291. lsmall = float(width)
  292. lfull = float(height - 1)
  293. if self.fMeterStyle == self.STYLE_OPENAV:
  294. painter.setPen(QColor(37, 37, 37, 100))
  295. painter.drawLine(QLineF(lsmall * 0.25, 2, lsmall * 0.25, lfull-2.0))
  296. painter.drawLine(QLineF(lsmall * 0.50, 2, lsmall * 0.50, lfull-2.0))
  297. painter.drawLine(QLineF(lsmall * 0.75, 2, lsmall * 0.75, lfull-2.0))
  298. if self.fChannelCount > 1:
  299. painter.drawLine(QLineF(1, lfull/2-1, lsmall-1, lfull/2-1))
  300. else:
  301. # Base
  302. painter.setBrush(Qt.black)
  303. painter.setPen(QPen(self.fMeterColorBaseAlt, 1))
  304. painter.drawLine(QLineF(lsmall * 0.25, 2, lsmall * 0.25, lfull-2.0))
  305. painter.drawLine(QLineF(lsmall * 0.50, 2, lsmall * 0.50, lfull-2.0))
  306. # Yellow
  307. painter.setPen(QColor(110, 110, 15, 100))
  308. painter.drawLine(QLineF(lsmall * 0.70, 2, lsmall * 0.70, lfull-2.0))
  309. painter.drawLine(QLineF(lsmall * 0.83, 2, lsmall * 0.83, lfull-2.0))
  310. # Orange
  311. painter.setPen(QColor(180, 110, 15, 100))
  312. painter.drawLine(QLineF(lsmall * 0.90, 2, lsmall * 0.90, lfull-2.0))
  313. # Red
  314. painter.setPen(QColor(110, 15, 15, 100))
  315. painter.drawLine(QLineF(lsmall * 0.96, 2, lsmall * 0.96, lfull-2.0))
  316. elif self.fMeterOrientation == self.VERTICAL:
  317. # Variables
  318. lsmall = float(height)
  319. lfull = float(width - 1)
  320. if self.fMeterStyle == self.STYLE_OPENAV:
  321. painter.setPen(QColor(37, 37, 37, 100))
  322. painter.drawLine(QLineF(2, lsmall - (lsmall * 0.25), lfull-2.0, lsmall - (lsmall * 0.25)))
  323. painter.drawLine(QLineF(2, lsmall - (lsmall * 0.50), lfull-2.0, lsmall - (lsmall * 0.50)))
  324. painter.drawLine(QLineF(2, lsmall - (lsmall * 0.75), lfull-2.0, lsmall - (lsmall * 0.75)))
  325. if self.fChannelCount > 1:
  326. painter.drawLine(QLineF(lfull/2-1, 1, lfull/2-1, lsmall-1))
  327. else:
  328. # Base
  329. painter.setBrush(Qt.black)
  330. painter.setPen(QPen(self.fMeterColorBaseAlt, 1))
  331. painter.drawLine(QLineF(2, lsmall - (lsmall * 0.25), lfull-2.0, lsmall - (lsmall * 0.25)))
  332. painter.drawLine(QLineF(2, lsmall - (lsmall * 0.50), lfull-2.0, lsmall - (lsmall * 0.50)))
  333. # Yellow
  334. painter.setPen(QColor(110, 110, 15, 100))
  335. painter.drawLine(QLineF(2, lsmall - (lsmall * 0.70), lfull-2.0, lsmall - (lsmall * 0.70)))
  336. painter.drawLine(QLineF(2, lsmall - (lsmall * 0.82), lfull-2.0, lsmall - (lsmall * 0.82)))
  337. # Orange
  338. painter.setPen(QColor(180, 110, 15, 100))
  339. painter.drawLine(QLineF(2, lsmall - (lsmall * 0.90), lfull-2.0, lsmall - (lsmall * 0.90)))
  340. # Red
  341. painter.setPen(QColor(110, 15, 15, 100))
  342. painter.drawLine(QLineF(2, lsmall - (lsmall * 0.96), lfull-2.0, lsmall - (lsmall * 0.96)))
  343. # -----------------------------------------------------------------------------------------------------------------
  344. def resizeEvent(self, event):
  345. QWidget.resizeEvent(self, event)
  346. self.updateGrandientFinalStop()
  347. # ---------------------------------------------------------------------------------------------------------------------