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.

digitalpeakmeter.py 18KB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 years ago
9 years ago
11 years ago
9 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 years ago
11 years ago
9 years ago
9 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
9 years ago
9 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 years ago
9 years ago
9 years ago
9 years ago
11 years ago
9 years ago
9 years ago
11 years ago
9 years ago
11 years ago
9 years ago
11 years ago
9 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 years ago
9 years ago
11 years ago
9 years ago
11 years ago
11 years ago
11 years ago
9 years ago
9 years ago
9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Digital Peak Meter, a custom Qt4 widget
  4. # Copyright (C) 2011-2015 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License as
  8. # published by the Free Software Foundation; either version 2 of
  9. # the License, or any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the doc/GPL.txt file.
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Config)
  19. from carla_config import *
  20. # ------------------------------------------------------------------------------------------------------------
  21. # Imports (Global)
  22. from math import sqrt
  23. if config_UseQt5:
  24. from PyQt5.QtCore import qCritical, Qt, QTimer, QSize
  25. from PyQt5.QtGui import QColor, QLinearGradient, QPainter, QPen, QPixmap
  26. from PyQt5.QtWidgets import QWidget
  27. else:
  28. from PyQt4.QtCore import qCritical, Qt, QTimer, QSize
  29. from PyQt4.QtGui import QColor, QLinearGradient, QPainter, QPen, QPixmap, QWidget
  30. # ------------------------------------------------------------------------------------------------------------
  31. # Widget Class
  32. class DigitalPeakMeter(QWidget):
  33. # enum Color
  34. COLOR_GREEN = 1
  35. COLOR_BLUE = 2
  36. # enum Orientation
  37. HORIZONTAL = 1
  38. VERTICAL = 2
  39. # enum Style
  40. STYLE_DEFAULT = 1
  41. STYLE_OPENAV = 2
  42. STYLE_RNCBC = 3
  43. STYLE_CALF = 4
  44. # --------------------------------------------------------------------------------------------------------
  45. def __init__(self, parent):
  46. QWidget.__init__(self, parent)
  47. self.setAttribute(Qt.WA_OpaquePaintEvent)
  48. # defaults are VERTICAL, COLOR_GREEN, STYLE_DEFAULT
  49. self.fChannelCount = 0
  50. self.fChannelData = []
  51. self.fLastChannelData = []
  52. self.fMeterColor = self.COLOR_GREEN
  53. self.fMeterColorBase = QColor(93, 231, 61)
  54. self.fMeterColorBaseAlt = QColor(15, 110, 15, 100)
  55. self.fMeterLinesEnabled = True
  56. self.fMeterOrientation = self.VERTICAL
  57. self.fMeterStyle = self.STYLE_DEFAULT
  58. self.fMeterBackground = QColor("#070707")
  59. self.fMeterGradient = QLinearGradient(0, 0, 0, 0)
  60. self.fMeterPixmaps = ()
  61. self.fSmoothMultiplier = 2
  62. self.updateGrandient()
  63. # --------------------------------------------------------------------------------------------------------
  64. def channelCount(self):
  65. return self.fChannelCount
  66. def setChannelCount(self, count):
  67. if self.fChannelCount == count:
  68. return
  69. if count < 0:
  70. return qCritical("DigitalPeakMeter::setChannelCount(%i) - channel count must be a positive integer or zero" % count)
  71. self.fChannelCount = count
  72. self.fChannelData = []
  73. self.fLastChannelData = []
  74. for x in range(count):
  75. self.fChannelData.append(0.0)
  76. self.fLastChannelData.append(0.0)
  77. if self.fMeterStyle == self.STYLE_CALF:
  78. if self.fChannelCount > 0:
  79. self.setFixedSize(100, 12*self.fChannelCount)
  80. else:
  81. self.setMinimumSize(0, 0)
  82. self.setMaximumSize(9999, 9999)
  83. # --------------------------------------------------------------------------------------------------------
  84. def meterColor(self):
  85. return self.fMeterColor
  86. def setMeterColor(self, color):
  87. if self.fMeterColor == color:
  88. return
  89. if color not in (self.COLOR_GREEN, self.COLOR_BLUE):
  90. return qCritical("DigitalPeakMeter::setMeterColor(%i) - invalid color" % color)
  91. if color == self.COLOR_GREEN:
  92. self.fMeterColorBase = QColor(93, 231, 61)
  93. self.fMeterColorBaseAlt = QColor(15, 110, 15, 100)
  94. elif color == self.COLOR_BLUE:
  95. self.fMeterColorBase = QColor(82, 238, 248)
  96. self.fMeterColorBaseAlt = QColor(15, 15, 110, 100)
  97. self.fMeterColor = color
  98. self.updateGrandient()
  99. # --------------------------------------------------------------------------------------------------------
  100. def meterLinesEnabled(self):
  101. return self.fMeterLinesEnabled
  102. def setMeterLinesEnabled(self, yesNo):
  103. if self.fMeterLinesEnabled == yesNo:
  104. return
  105. self.fMeterLinesEnabled = yesNo
  106. # --------------------------------------------------------------------------------------------------------
  107. def meterOrientation(self):
  108. return self.fMeterOrientation
  109. def setMeterOrientation(self, orientation):
  110. if self.fMeterOrientation == orientation:
  111. return
  112. if orientation not in (self.HORIZONTAL, self.VERTICAL):
  113. return qCritical("DigitalPeakMeter::setMeterOrientation(%i) - invalid orientation" % orientation)
  114. self.fMeterOrientation = orientation
  115. self.updateGrandient()
  116. # --------------------------------------------------------------------------------------------------------
  117. def meterStyle(self):
  118. return self.fMeterStyle
  119. def setMeterStyle(self, style):
  120. if self.fMeterStyle == style:
  121. return
  122. if style not in (self.STYLE_DEFAULT, self.STYLE_OPENAV, self.STYLE_RNCBC, self.STYLE_CALF):
  123. return qCritical("DigitalPeakMeter::setMeterStyle(%i) - invalid style" % style)
  124. if style == self.STYLE_DEFAULT:
  125. self.fMeterBackground = QColor("#070707")
  126. elif style == self.STYLE_OPENAV:
  127. self.fMeterBackground = QColor("#1A1A1A")
  128. elif style == self.STYLE_RNCBC:
  129. self.fMeterBackground = QColor("#070707")
  130. elif style == self.STYLE_CALF:
  131. self.fMeterBackground = QColor("#000")
  132. if style == self.STYLE_CALF:
  133. self.fMeterPixmaps = (QPixmap(":/bitmaps/meter_calf_off.png"), QPixmap(":/bitmaps/meter_calf_on.png"))
  134. if self.fChannelCount > 0:
  135. self.setFixedSize(100, 12*self.fChannelCount)
  136. else:
  137. self.fMeterPixmaps = ()
  138. self.setMinimumSize(0, 0)
  139. self.setMaximumSize(9999, 9999)
  140. self.fMeterStyle = style
  141. self.updateGrandient()
  142. # --------------------------------------------------------------------------------------------------------
  143. def smoothMultiplier(self):
  144. return self.fSmoothMultiplier
  145. def setSmoothMultiplier(self, value):
  146. if self.fSmoothMultiplier == value:
  147. return
  148. if not isinstance(value, int):
  149. return qCritical("DigitalPeakMeter::setSmoothMultiplier() - value must be an integer")
  150. if value < 0:
  151. return qCritical("DigitalPeakMeter::setSmoothMultiplier(%i) - value must be >= 0" % value)
  152. if value > 5:
  153. return qCritical("DigitalPeakMeter::setSmoothMultiplier(%i) - value must be < 5" % value)
  154. self.fSmoothMultiplier = value
  155. # --------------------------------------------------------------------------------------------------------
  156. def displayMeter(self, meter, level, forced = False):
  157. if not isinstance(meter, int):
  158. return qCritical("DigitalPeakMeter::displayMeter(,) - meter value must be an integer")
  159. if not isinstance(level, float):
  160. return qCritical("DigitalPeakMeter::displayMeter(%i,) - level value must be a float" % (meter,))
  161. if meter <= 0 or meter > self.fChannelCount:
  162. return qCritical("DigitalPeakMeter::displayMeter(%i, %f) - invalid meter number" % (meter, level))
  163. i = meter - 1
  164. if self.fSmoothMultiplier > 0 and not forced:
  165. level = (self.fLastChannelData[i] * float(self.fSmoothMultiplier) + level) / float(self.fSmoothMultiplier + 1)
  166. if level < 0.001:
  167. level = 0.0
  168. elif level > 0.999:
  169. level = 1.0
  170. if self.fChannelData[i] != level:
  171. self.fChannelData[i] = level
  172. self.update()
  173. self.fLastChannelData[i] = level
  174. # --------------------------------------------------------------------------------------------------------
  175. def updateGrandient(self):
  176. self.fMeterGradient = QLinearGradient(0, 0, 1, 1)
  177. if self.fMeterStyle == self.STYLE_DEFAULT:
  178. if self.fMeterOrientation == self.HORIZONTAL:
  179. self.fMeterGradient.setColorAt(0.0, self.fMeterColorBase)
  180. self.fMeterGradient.setColorAt(0.2, self.fMeterColorBase)
  181. self.fMeterGradient.setColorAt(0.4, self.fMeterColorBase)
  182. self.fMeterGradient.setColorAt(0.6, self.fMeterColorBase)
  183. self.fMeterGradient.setColorAt(0.8, Qt.yellow)
  184. self.fMeterGradient.setColorAt(1.0, Qt.red)
  185. elif self.fMeterOrientation == self.VERTICAL:
  186. self.fMeterGradient.setColorAt(0.0, Qt.red)
  187. self.fMeterGradient.setColorAt(0.2, Qt.yellow)
  188. self.fMeterGradient.setColorAt(0.4, self.fMeterColorBase)
  189. self.fMeterGradient.setColorAt(0.6, self.fMeterColorBase)
  190. self.fMeterGradient.setColorAt(0.8, self.fMeterColorBase)
  191. self.fMeterGradient.setColorAt(1.0, self.fMeterColorBase)
  192. elif self.fMeterStyle == self.STYLE_RNCBC:
  193. if self.fMeterColor == self.COLOR_BLUE:
  194. c1 = QColor(40,160,160)
  195. c2 = QColor(60,220,160)
  196. elif self.fMeterColor == self.COLOR_GREEN:
  197. c1 = QColor( 40,160,40)
  198. c2 = QColor(160,220,20)
  199. if self.fMeterOrientation == self.HORIZONTAL:
  200. self.fMeterGradient.setColorAt(0.0, c1)
  201. self.fMeterGradient.setColorAt(0.2, c1)
  202. self.fMeterGradient.setColorAt(0.6, c2)
  203. self.fMeterGradient.setColorAt(0.7, QColor(220,220, 20))
  204. self.fMeterGradient.setColorAt(0.8, QColor(240,160, 20))
  205. self.fMeterGradient.setColorAt(0.9, QColor(240, 0, 20))
  206. self.fMeterGradient.setColorAt(1.0, QColor(240, 0, 20))
  207. elif self.fMeterOrientation == self.VERTICAL:
  208. self.fMeterGradient.setColorAt(0.0, QColor(240, 0, 20))
  209. self.fMeterGradient.setColorAt(0.1, QColor(240, 0, 20))
  210. self.fMeterGradient.setColorAt(0.2, QColor(240,160, 20))
  211. self.fMeterGradient.setColorAt(0.3, QColor(220,220, 20))
  212. self.fMeterGradient.setColorAt(0.4, c2)
  213. self.fMeterGradient.setColorAt(0.8, c1)
  214. self.fMeterGradient.setColorAt(1.0, c1)
  215. elif self.fMeterStyle in (self.STYLE_OPENAV, self.STYLE_CALF):
  216. self.fMeterGradient.setColorAt(0.0, self.fMeterColorBase)
  217. self.fMeterGradient.setColorAt(1.0, self.fMeterColorBase)
  218. self.updateGrandientFinalStop()
  219. def updateGrandientFinalStop(self):
  220. if self.fMeterOrientation == self.HORIZONTAL:
  221. self.fMeterGradient.setFinalStop(self.width(), 0)
  222. elif self.fMeterOrientation == self.VERTICAL:
  223. self.fMeterGradient.setFinalStop(0, self.height())
  224. # --------------------------------------------------------------------------------------------------------
  225. def minimumSizeHint(self):
  226. return QSize(10, 10)
  227. def sizeHint(self):
  228. return QSize(self.width(), self.height())
  229. # --------------------------------------------------------------------------------------------------------
  230. def drawCalf(self, event):
  231. painter = QPainter(self)
  232. event.accept()
  233. # no channels, draw black
  234. if self.fChannelCount == 0:
  235. painter.setPen(QPen(Qt.black, 2))
  236. painter.setBrush(Qt.black)
  237. painter.drawRect(0, 0, self.width(), self.height())
  238. return
  239. for i in range(self.fChannelCount):
  240. painter.drawPixmap(0, 12*i, self.fMeterPixmaps[0])
  241. meterPos = 4
  242. meterSize = 12
  243. # draw levels
  244. for level in self.fChannelData:
  245. if level != 0.0:
  246. blevel = int(sqrt(level)*26.0)*3
  247. painter.drawPixmap(5, meterPos, blevel, 4, self.fMeterPixmaps[1], 0, 0, blevel, 4)
  248. meterPos += meterSize
  249. def paintEvent(self, event):
  250. if self.fMeterStyle == self.STYLE_CALF:
  251. return self.drawCalf(event)
  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(0, meterPos, int(sqrt(level) * float(width)), meterSize)
  283. elif self.fMeterOrientation == self.VERTICAL:
  284. painter.drawRect(meterPos, height - int(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(lsmall * 0.25, 2, lsmall * 0.25, lfull-2.0)
  296. painter.drawLine(lsmall * 0.50, 2, lsmall * 0.50, lfull-2.0)
  297. painter.drawLine(lsmall * 0.75, 2, lsmall * 0.75, lfull-2.0)
  298. if self.fChannelCount > 1:
  299. painter.drawLine(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(lsmall * 0.25, 2, lsmall * 0.25, lfull-2.0)
  305. painter.drawLine(lsmall * 0.50, 2, lsmall * 0.50, lfull-2.0)
  306. # Yellow
  307. painter.setPen(QColor(110, 110, 15, 100))
  308. painter.drawLine(lsmall * 0.70, 2, lsmall * 0.70, lfull-2.0)
  309. painter.drawLine(lsmall * 0.83, 2, lsmall * 0.83, lfull-2.0)
  310. # Orange
  311. painter.setPen(QColor(180, 110, 15, 100))
  312. painter.drawLine(lsmall * 0.90, 2, lsmall * 0.90, lfull-2.0)
  313. # Red
  314. painter.setPen(QColor(110, 15, 15, 100))
  315. painter.drawLine(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(2, lsmall - (lsmall * 0.25), lfull-2.0, lsmall - (lsmall * 0.25))
  323. painter.drawLine(2, lsmall - (lsmall * 0.50), lfull-2.0, lsmall - (lsmall * 0.50))
  324. painter.drawLine(2, lsmall - (lsmall * 0.75), lfull-2.0, lsmall - (lsmall * 0.75))
  325. if self.fChannelCount > 1:
  326. painter.drawLine(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(2, lsmall - (lsmall * 0.25), lfull-2.0, lsmall - (lsmall * 0.25))
  332. painter.drawLine(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(2, lsmall - (lsmall * 0.70), lfull-2.0, lsmall - (lsmall * 0.70))
  336. painter.drawLine(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(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(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. # ------------------------------------------------------------------------------------------------------------
  348. # Main Testing
  349. if __name__ == '__main__':
  350. import sys
  351. import resources_rc
  352. if config_UseQt5:
  353. from PyQt5.QtWidgets import QApplication
  354. else:
  355. from PyQt4.QtGui import QApplication
  356. app = QApplication(sys.argv)
  357. gui = DigitalPeakMeter(None)
  358. gui.setChannelCount(2)
  359. #gui.setMeterOrientation(DigitalPeakMeter.HORIZONTAL)
  360. gui.setMeterStyle(DigitalPeakMeter.STYLE_RNCBC)
  361. gui.displayMeter(1, 0.5)
  362. gui.displayMeter(2, 0.8)
  363. gui.show()
  364. sys.exit(app.exec_())
  365. # ------------------------------------------------------------------------------------------------------------