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 16KB

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