diff options
Diffstat (limited to 'voctogui/lib/audioleveldisplay.py')
-rw-r--r-- | voctogui/lib/audioleveldisplay.py | 242 |
1 files changed, 124 insertions, 118 deletions
diff --git a/voctogui/lib/audioleveldisplay.py b/voctogui/lib/audioleveldisplay.py index 1a4be14..bd8fb21 100644 --- a/voctogui/lib/audioleveldisplay.py +++ b/voctogui/lib/audioleveldisplay.py @@ -1,121 +1,127 @@ -import logging, math +import logging +import math from gi.repository import Gst, Gtk + class AudioLevelDisplay(object): - """ Displays a Level-Meter of another VideoDisplay into a GtkWidget """ - - def __init__(self, drawing_area): - self.log = logging.getLogger('AudioLevelDisplay[%s]' % drawing_area.get_name()) - - self.drawing_area = drawing_area - - self.levelrms = [] - self.levelpeak = [] - self.leveldecay = [] - - # register on_draw handler - self.drawing_area.connect('draw', self.on_draw) - - def on_draw(self, widget, cr): - # number of audio-channels - channels = len(self.levelrms) - - if channels == 0: - return - - width = self.drawing_area.get_allocated_width() - height = self.drawing_area.get_allocated_height() - - # space between the channels in px - margin = 2 - - # 1 channel -> 0 margins, 2 channels -> 1 margin, 3 channels… - channel_width = int( (width - (margin * (channels - 1))) / channels ) - - # self.log.debug( - # 'width: %upx filled with %u channels of each %upx ' - # 'and %ux margin of %upx', - # width, channels, channel_width, channels-1, margin) - - # normalize db-value to 0…1 and multiply with the height - rms_px = [ self.normalize_db(db) * height for db in self.levelrms ] - peak_px = [ self.normalize_db(db) * height for db in self.levelpeak ] - decay_px = [ self.normalize_db(db) * height for db in self.leveldecay ] - - # set the line-width >1, to get a nice overlap - cr.set_line_width(2) - - # iterate over all pixels - for y in range(0, height): - - # calculate our place in the color-gradient, clamp to 0…1 - # 0 -> green, 0.5 -> yellow, 1 -> red - color = self.clamp(((y / height) - 0.6) / 0.42) - - for channel in range(0, channels): - # start-coordinate for this channel - x = (channel * channel_width) + (channel * margin) - - # calculate the brightness based on whether this line is in the - # active region - - # default to 0.25, dark - bright = 0.25 - if int(y - decay_px[channel]) in range(0, 2): - # decay marker, 2px wide, extra bright - bright = 1.5 - elif y < rms_px[channel]: - # rms bar, full bright - bright = 1 - elif y < peak_px[channel]: - # peak bar, a little darker - bright = 0.75 - - # set the color with a little reduced green - cr.set_source_rgb( - color * bright, - (1-color) * bright * 0.75, - 0 - ) - - # draw the marker - cr.move_to(x, height-y) - cr.line_to(x + channel_width, height-y) - cr.stroke() - - # draw a black line for the margin - cr.set_source_rgb(0,0,0) - cr.move_to(x + channel_width, height-y) - cr.line_to(x + channel_width + margin, height-y) - cr.stroke() - - # draw db text-markers - cr.set_source_rgb(1, 1, 1) - for db in [-40, -20, -10, -5, -4, -3, -2, -1]: - text = str(db) - xbearing, ybearing, textwidth, textheight, xadvance, yadvance = ( - cr.text_extents(text)) - - y = self.normalize_db(db) * height - cr.move_to((width-textwidth) / 2, height - y - textheight) - cr.show_text(text) - - return True - - def normalize_db(self, db): - # -60db -> 1.00 (very quiet) - # -30db -> 0.75 - # -15db -> 0.50 - # -5db -> 0.25 - # -0db -> 0.00 (very loud) - logscale = 1 - math.log10(-0.15 * db + 1) - return self.clamp(logscale) - - def clamp(self, value, min_value=0, max_value=1): - return max(min(value, max_value), min_value) - - def level_callback(self, rms, peak, decay): - self.levelrms = rms - self.levelpeak = peak - self.leveldecay = decay - self.drawing_area.queue_draw() + """Displays a Level-Meter of another VideoDisplay into a GtkWidget""" + + def __init__(self, drawing_area): + self.log = logging.getLogger( + 'AudioLevelDisplay[{}]'.format(drawing_area.get_name()) + ) + + self.drawing_area = drawing_area + + self.levelrms = [] + self.levelpeak = [] + self.leveldecay = [] + + # register on_draw handler + self.drawing_area.connect('draw', self.on_draw) + + def on_draw(self, widget, cr): + # number of audio-channels + channels = len(self.levelrms) + + if channels == 0: + return + + width = self.drawing_area.get_allocated_width() + height = self.drawing_area.get_allocated_height() + + # space between the channels in px + margin = 2 + + # 1 channel -> 0 margins, 2 channels -> 1 margin, 3 channels… + channel_width = int((width - (margin * (channels - 1))) / channels) + + # self.log.debug( + # 'width: %upx filled with %u channels of each %upx ' + # 'and %ux margin of %upx', + # width, channels, channel_width, channels - 1, margin + # ) + + # normalize db-value to 0…1 and multiply with the height + rms_px = [self.normalize_db(db) * height for db in self.levelrms] + peak_px = [self.normalize_db(db) * height for db in self.levelpeak] + decay_px = [self.normalize_db(db) * height for db in self.leveldecay] + + # set the line-width >1, to get a nice overlap + cr.set_line_width(2) + + # iterate over all pixels + for y in range(0, height): + + # calculate our place in the color-gradient, clamp to 0…1 + # 0 -> green, 0.5 -> yellow, 1 -> red + color = self.clamp(((y / height) - 0.6) / 0.42) + + for channel in range(0, channels): + # start-coordinate for this channel + x = (channel * channel_width) + (channel * margin) + + # calculate the brightness based on whether this line is in the + # active region + + # default to 0.25, dark + bright = 0.25 + if int(y - decay_px[channel]) in range(0, 2): + # decay marker, 2px wide, extra bright + bright = 1.5 + elif y < rms_px[channel]: + # rms bar, full bright + bright = 1 + elif y < peak_px[channel]: + # peak bar, a little darker + bright = 0.75 + + # set the color with a little reduced green + cr.set_source_rgb( + color * bright, + (1 - color) * bright * 0.75, + 0 + ) + + # draw the marker + cr.move_to(x, height - y) + cr.line_to(x + channel_width, height - y) + cr.stroke() + + # draw a black line for the margin + cr.set_source_rgb(0, 0, 0) + cr.move_to(x + channel_width, height - y) + cr.line_to(x + channel_width + margin, height - y) + cr.stroke() + + # draw db text-markers + cr.set_source_rgb(1, 1, 1) + for db in [-40, -20, -10, -5, -4, -3, -2, -1]: + text = str(db) + (xbearing, ybearing, + textwidth, textheight, + xadvance, yadvance) = cr.text_extents(text) + + y = self.normalize_db(db) * height + cr.move_to((width - textwidth) / 2, height - y - textheight) + cr.show_text(text) + + return True + + def normalize_db(self, db): + # -60db -> 1.00 (very quiet) + # -30db -> 0.75 + # -15db -> 0.50 + # -5db -> 0.25 + # -0db -> 0.00 (very loud) + logscale = 1 - math.log10(-0.15 * db + 1) + return self.clamp(logscale) + + def clamp(self, value, min_value=0, max_value=1): + return max(min(value, max_value), min_value) + + def level_callback(self, rms, peak, decay): + self.levelrms = rms + self.levelpeak = peak + self.leveldecay = decay + self.drawing_area.queue_draw() |