2
# vi:si:et:sw=4:sts=4:ts=4
5
# Copyright (C) 2005 Fluendo S.L.
6
# Originally from the Flumotion streaming server.
8
# This library is free software; you can redistribute it and/or
9
# modify it under the terms of the GNU Library General Public
10
# License as published by the Free Software Foundation; either
11
# version 2 of the License, or (at your option) any later version.
13
# This library is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
# Library General Public License for more details.
18
# You should have received a copy of the GNU Library General Public
19
# License along with this library; if not, write to the
20
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21
# Boston, MA 02111-1307, USA.
23
# Author: Zaheer Merali <zaheermerali at gmail dot com>
30
# this VUMeter respects IEC standard
31
# BS 6840-18:1996/IEC-268-18
32
# and is inspired by JACK's meterbridge dpm_meters.c
34
class FVUMeter(gtk.DrawingArea):
35
__gsignals__ = { 'expose-event' : 'override',
36
'size-allocate': 'override',
37
'size-request': 'override',
38
'realize' : 'override'
41
'peak' : (gobject.TYPE_FLOAT,
43
'peak volume level in dB',
47
gobject.PARAM_READWRITE),
48
'decay' : (gobject.TYPE_FLOAT,
50
'decay volume level in dB',
54
gobject.PARAM_READWRITE),
55
'orange-threshold': (gobject.TYPE_FLOAT,
56
'threshold for orange',
57
'threshold for orange use in dB',
61
gobject.PARAM_READWRITE),
62
'red-threshold': (gobject.TYPE_FLOAT,
64
'threshold for red use in dB',
68
gobject.PARAM_READWRITE)
79
orange_threshold = -10.0
85
# Returns the meter deflection percentage given a db value
86
def iec_scale(self, db):
92
pct = (db + 70.0) * 0.25
94
pct = (db + 60.0) * 0.5 + 2.5
96
pct = (db + 50.0) * 0.75 + 7.5
98
pct = (db + 40.0) * 1.5 + 15.0
100
pct = (db + 30.0) * 2.0 + 30.0
102
pct = (db + 20.0) * 2.5 + 50.0
108
def do_get_property(self, property):
109
if property.name == 'peak':
110
return self.peaklevel
111
elif property.name == 'decay':
112
return self.decaylevel
113
elif property.name == 'orange-threshold':
114
return self.orange_threshold
115
elif property.name == 'red-threshold':
116
return self.red_threshold
118
raise AttributeError, 'unknown property %s' % property.name
120
def do_set_property(self, property, value):
121
if property.name == 'peak':
122
self.peaklevel = value
123
elif property.name == 'decay':
124
self.decaylevel = value
125
elif property.name == 'orange-threshold':
126
self.orange_threshold = value
127
elif property.name == 'red-threshold':
128
self.red_threshold = value
130
raise AttributeError, 'unknown property %s' % property.name
134
def do_size_request(self, requisition):
135
requisition.width = 250
136
requisition.height = 50
138
def do_size_allocate(self, allocation):
139
self.allocation = allocation
140
if self.flags() & gtk.REALIZED:
141
self.window.move_resize(*allocation)
143
def do_realize(self):
144
self.set_flags(self.flags() | gtk.REALIZED)
146
self.window = gdk.Window(self.get_parent_window(),
147
width=self.allocation.width,
148
height=self.allocation.height,
149
window_type=gdk.WINDOW_CHILD,
150
wclass=gdk.INPUT_OUTPUT,
151
event_mask=self.get_events() | gdk.EXPOSURE_MASK)
153
colormap = gtk.gdk.colormap_get_system()
154
green = colormap.alloc_color(0, 65535, 0)
155
orange = colormap.alloc_color(65535, 32768, 0)
156
red = colormap.alloc_color(65535, 0, 0)
157
yellow = colormap.alloc_color(65535, 65535, 0)
158
self.green_gc = gdk.GC(self.window, foreground=green)
159
self.orange_gc = gdk.GC(self.window, foreground=orange)
160
self.red_gc = gdk.GC(self.window, foreground=red)
161
self.yellow_gc = gdk.GC(self.window, foreground=yellow)
163
self.window.set_user_data(self)
164
self.style.attach(self.window)
165
self.style.set_background(self.window, gtk.STATE_NORMAL)
167
def do_expose_event(self, event):
170
x, y, w, h = self.allocation
171
vumeter_width = w - (self.leftborder + self.rightborder)
172
vumeter_height = h - (self.topborder + self.bottomborder)
173
self.window.draw_rectangle(self.style.black_gc, True,
174
self.leftborder, self.topborder,
178
# 0 maps to width of 0, full scale maps to total width
179
peaklevelpct = self.iec_scale(self.peaklevel)
180
peakwidth = int(vumeter_width * (peaklevelpct / 100))
181
draw_gc = self.green_gc
182
if self.peaklevel >= self.orange_threshold:
183
draw_gc = self.orange_gc
184
if self.peaklevel >= self.red_threshold:
185
draw_gc = self.red_gc
187
self.window.draw_rectangle(draw_gc, True,
188
self.leftborder, self.topborder,
189
peakwidth, vumeter_height)
191
# draw yellow decay level
192
if self.decaylevel > -90.0:
193
decaylevelpct = self.iec_scale(self.decaylevel)
194
decaywidth = int(vumeter_width * (decaylevelpct / 100))
195
# cheat the geometry by drawing 0% level at pixel 0,
196
# which is same position as just above 0%
199
self.window.draw_line(self.yellow_gc,
200
self.leftborder + decaywidth - 1,
202
self.leftborder + decaywidth - 1,
203
self.topborder + vumeter_height - 1)
215
for level, scale in scalers:
216
# tick mark, 6 pixels high
217
# we cheat again here by putting the 0 at the first pixel
218
self.window.draw_line(self.style.black_gc,
219
self.leftborder + int(scale * (vumeter_width - 1)),
220
h - self.bottomborder,
221
self.leftborder + int(scale * (vumeter_width - 1)),
222
h - self.bottomborder + 5)
224
layout = self.create_pango_layout(level)
225
layout_width, layout_height = layout.get_pixel_size()
226
self.window.draw_layout(self.style.black_gc,
227
self.leftborder + int(scale * vumeter_width)
228
- int(layout_width / 2),
229
h - self.bottomborder + 7, layout)
231
# draw the peak level to the right
232
layout = self.create_pango_layout("%.2fdB" % self.peaklevel)
233
layout_width, layout_height = layout.get_pixel_size()
234
self.window.draw_layout(self.style.black_gc,
235
self.leftborder + vumeter_width + 5,
236
self.topborder + int(vumeter_height / 2 - layout_height / 2),
239
gobject.type_register(FVUMeter)