xref: /haiku/src/apps/mediaplayer/interface/PeakView.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
1 /*
2  * Copyright (C) 2001-2010 Stephan Aßmus. All rights reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Copyright (C) 1998-1999 Be Incorporated. All rights reseved.
6  * Distributed under the terms of the Be Sample Code license.
7  */
8 
9 
10 #include "PeakView.h"
11 
12 #include <new>
13 #include <stdio.h>
14 #include <string.h>
15 
16 #include <Bitmap.h>
17 #include <Catalog.h>
18 #include <ControlLook.h>
19 #include <Locale.h>
20 #include <MenuItem.h>
21 #include <Message.h>
22 #include <MessageRunner.h>
23 #include <Messenger.h>
24 #include <PopUpMenu.h>
25 #include <Window.h>
26 
27 
28 #undef B_TRANSLATE_CONTEXT
29 #define B_TRANSLATE_CONTEXT "MediaPlayer-PeakView"
30 
31 
32 using std::nothrow;
33 
34 
35 enum {
36 	MSG_PULSE		= 'puls',
37 	MSG_LOCK_PEAKS	= 'lpks'
38 };
39 
40 
41 PeakView::PeakView(const char* name, bool useGlobalPulse, bool displayLabels)
42 	:
43 	BView(name, (useGlobalPulse ? B_PULSE_NEEDED : 0)
44 		| B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE),
45 	fUseGlobalPulse(useGlobalPulse),
46 	fDisplayLabels(displayLabels),
47 	fPeakLocked(false),
48 
49 	fRefreshDelay(20000),
50 	fPulse(NULL),
51 
52 	fChannelInfos(NULL),
53 	fChannelCount(0),
54 	fGotData(true),
55 
56 	fBackBitmap(NULL),
57 	fPeakNotificationWhat(0)
58 {
59 	GetFontHeight(&fFontHeight);
60 
61 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
62 	SetViewColor(B_TRANSPARENT_COLOR);
63 
64 	SetChannelCount(2);
65 }
66 
67 
68 PeakView::~PeakView()
69 {
70 	delete fPulse;
71 	delete fBackBitmap;
72 	delete[] fChannelInfos;
73 }
74 
75 
76 void
77 PeakView::MessageReceived(BMessage* message)
78 {
79 	if (message->what == fPeakNotificationWhat) {
80 		float max;
81 		for (int32 i = 0; message->FindFloat("max", i, &max) == B_OK; i++)
82 			SetMax(max, i);
83 		fGotData = true;
84 		return;
85 	}
86 
87 	switch (message->what) {
88 		case MSG_PULSE:
89 			Pulse();
90 			break;
91 
92 		case MSG_LOCK_PEAKS:
93 			fPeakLocked = !fPeakLocked;
94 			break;
95 
96 		default:
97 			BView::MessageReceived(message);
98 			break;
99 	}
100 }
101 
102 
103 void
104 PeakView::AttachedToWindow()
105 {
106 	if (!fUseGlobalPulse) {
107 		delete fPulse;
108 		BMessage message(MSG_PULSE);
109 		fPulse = new BMessageRunner(BMessenger(this), &message,
110 			fRefreshDelay);
111 	}
112 }
113 
114 
115 void
116 PeakView::DetachedFromWindow()
117 {
118 	delete fPulse;
119 	fPulse = NULL;
120 }
121 
122 
123 void
124 PeakView::MouseDown(BPoint where)
125 {
126 	int32 buttons;
127 	if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) < B_OK)
128 		buttons = B_PRIMARY_MOUSE_BUTTON;
129 
130 	if (buttons & B_PRIMARY_MOUSE_BUTTON) {
131 		// Reset the overshot flag and set the observed max to the current
132 		// value.
133 		for (uint32 i = 0; i < fChannelCount; i++) {
134 			fChannelInfos[i].last_overshot_time = -5000000;
135 			fChannelInfos[i].last_max = fChannelInfos[i].current_max;
136 		}
137 	} else if (buttons & B_TERTIARY_MOUSE_BUTTON) {
138 		// Toggle locking of the observed max value.
139 		fPeakLocked = !fPeakLocked;
140 	} else {
141 		// Display context menu
142 		BPopUpMenu* menu = new BPopUpMenu("peak context menu");
143 		BMenuItem* item = new BMenuItem(B_TRANSLATE("Lock Peaks"),
144 			new BMessage(MSG_LOCK_PEAKS));
145 		item->SetMarked(fPeakLocked);
146 		menu->AddItem(item);
147 		menu->SetTargetForItems(this);
148 
149 		menu->SetAsyncAutoDestruct(true);
150 		menu->SetFont(be_plain_font);
151 
152 		where = ConvertToScreen(where);
153 		bool keepOpen = false; // ?
154 		if (keepOpen) {
155 			BRect mouseRect(where, where);
156 			mouseRect.InsetBy(-3.0, -3.0);
157 			where += BPoint(3.0, 3.0);
158 			menu->Go(where, true, false, mouseRect, true);
159 		} else {
160 			where += BPoint(3.0, 3.0);
161 			menu->Go(where, true, false, true);
162 		}
163 	}
164 }
165 
166 
167 void
168 PeakView::Draw(BRect updateRect)
169 {
170 	BRect r(_BackBitmapFrame());
171 	float width = r.Width();
172 	r.InsetBy(-2.0, -2.0);
173 
174 	be_control_look->DrawTextControlBorder(this, r, updateRect, LowColor());
175 
176 	// peak bitmap
177 	if (fBackBitmap)
178 		_DrawBitmap();
179 
180 	// dB labels
181 	if (fDisplayLabels) {
182 		font_height fh;
183 		GetFontHeight(&fh);
184 		float y = Bounds().bottom;
185 		y -= fh.descent;
186 		DrawString("0", BPoint(4.0 + width - StringWidth("0"), y));
187 		DrawString("-6", BPoint(0.477 * width, y));
188 		DrawString("-12", BPoint(0.227 * width, y));
189 	}
190 }
191 
192 
193 void
194 PeakView::FrameResized(float width, float height)
195 {
196 	BRect bitmapFrame = _BackBitmapFrame();
197 	_ResizeBackBitmap(bitmapFrame.IntegerWidth() + 1, fChannelCount);
198 	_UpdateBackBitmap();
199 }
200 
201 
202 void
203 PeakView::Pulse()
204 {
205 	if (!fGotData)
206 		return;
207 
208 	if (fBackBitmap == NULL)
209 		return;
210 
211 	if (!fPeakLocked) {
212 		for (uint32 i = 0; i < fChannelCount; i++) {
213 			fChannelInfos[i].last_max *= 0.96f;
214 			if (fChannelInfos[i].current_max > fChannelInfos[i].last_max)
215 				fChannelInfos[i].last_max = fChannelInfos[i].current_max;
216 		}
217 	}
218 	_UpdateBackBitmap();
219 
220 	for (uint32 i = 0; i < fChannelCount; i++)
221 		fChannelInfos[i].current_max = 0.0f;
222 	fGotData = false;
223 
224 	_DrawBitmap();
225 	Flush();
226 }
227 
228 
229 BSize
230 PeakView::MinSize()
231 {
232 	float minWidth = 20 + 4;
233 	float minHeight = 2 * 8 - 1 + 4;
234 	if (fDisplayLabels) {
235 		font_height fh;
236 		GetFontHeight(&fh);
237 		minWidth = max_c(60.0, minWidth);
238 		minHeight += ceilf(fh.ascent + fh.descent);
239 	}
240 	return BSize(minWidth, minHeight);
241 }
242 
243 
244 bool
245 PeakView::IsValid() const
246 {
247 	return fBackBitmap != NULL && fBackBitmap->IsValid()
248 		&& fChannelInfos != NULL;
249 }
250 
251 
252 void
253 PeakView::SetPeakRefreshDelay(bigtime_t delay)
254 {
255 	if (fRefreshDelay == delay)
256 		return;
257 
258 	fRefreshDelay = delay;
259 
260 	if (fPulse != NULL)
261 		fPulse->SetInterval(fRefreshDelay);
262 }
263 
264 
265 void
266 PeakView::SetPeakNotificationWhat(uint32 what)
267 {
268 	fPeakNotificationWhat = what;
269 }
270 
271 
272 void
273 PeakView::SetChannelCount(uint32 channelCount)
274 {
275 	if (channelCount == fChannelCount)
276 		return;
277 
278 	delete[] fChannelInfos;
279 	fChannelInfos = new(std::nothrow) ChannelInfo[channelCount];
280 	if (fChannelInfos != NULL) {
281 		fChannelCount = channelCount;
282 		for (uint32 i = 0; i < fChannelCount; i++) {
283 			fChannelInfos[i].current_max = 0.0f;
284 			fChannelInfos[i].last_max = 0.0f;
285 			fChannelInfos[i].last_overshot_time = -5000000;
286 		}
287 		_ResizeBackBitmap(_BackBitmapFrame().IntegerWidth() + 1,
288 			fChannelCount);
289 	} else
290 		fChannelCount = 0;
291 }
292 
293 
294 void
295 PeakView::SetMax(float max, uint32 channel)
296 {
297 	if (channel >= fChannelCount)
298 		return;
299 
300 	if (fChannelInfos[channel].current_max < max)
301 		fChannelInfos[channel].current_max = max;
302 
303 	if (fChannelInfos[channel].current_max > 1.0)
304 		fChannelInfos[channel].last_overshot_time = system_time();
305 }
306 
307 
308 // #pragma mark -
309 
310 
311 BRect
312 PeakView::_BackBitmapFrame() const
313 {
314 	BRect frame = Bounds();
315 	frame.InsetBy(2, 2);
316 	if (fDisplayLabels)
317 		frame.bottom -= ceilf(fFontHeight.ascent + fFontHeight.descent);
318 	return frame;
319 }
320 
321 
322 void
323 PeakView::_ResizeBackBitmap(int32 width, int32 channels)
324 {
325 	if (fBackBitmap != NULL) {
326 		if (fBackBitmap->Bounds().IntegerWidth() + 1 == width
327 			&& fBackBitmap->Bounds().IntegerHeight() + 1 == channels) {
328 			return;
329 		}
330 	}
331 	if (channels <= 0)
332 		channels = 2;
333 
334 	delete fBackBitmap;
335 	BRect bounds(0, 0, width - 1, channels - 1);
336 	fBackBitmap = new(std::nothrow) BBitmap(bounds, 0, B_RGB32);
337 	if (fBackBitmap == NULL || !fBackBitmap->IsValid()) {
338 		delete fBackBitmap;
339 		fBackBitmap = NULL;
340 		return;
341 	}
342 	memset(fBackBitmap->Bits(), 0, fBackBitmap->BitsLength());
343 	fGotData = true;
344 }
345 
346 
347 void
348 PeakView::_UpdateBackBitmap()
349 {
350 	if (!fBackBitmap)
351 		return;
352 
353 	uint8* span = (uint8*)fBackBitmap->Bits();
354 	uint32 width = fBackBitmap->Bounds().IntegerWidth() + 1;
355 	for (uint32 i = 0; i < fChannelCount; i++) {
356 		_RenderSpan(span, width, fChannelInfos[i].current_max,
357 			fChannelInfos[i].last_max,
358 			system_time() - fChannelInfos[i].last_overshot_time < 2000000);
359 		span += fBackBitmap->BytesPerRow();
360 	}
361 }
362 
363 
364 void
365 PeakView:: _RenderSpan(uint8* span, uint32 width, float current, float peak,
366 	bool overshot)
367 {
368 	uint8 emptyR = 15;
369 	uint8 emptyG = 36;
370 	uint8 emptyB = 16;
371 
372 	uint8 fillR = 41;
373 	uint8 fillG = 120;
374 	uint8 fillB = 45;
375 
376 	uint8 currentR = 45;
377 	uint8 currentG = 255;
378 	uint8 currentB = 45;
379 
380 	uint8 lastR = 255;
381 	uint8 lastG = 229;
382 	uint8 lastB = 87;
383 
384 	uint8 overR = 255;
385 	uint8 overG = 89;
386 	uint8 overB = 7;
387 
388 	uint8 kFadeFactor = 100;
389 
390 	uint32 evenWidth = width - width % 2;
391 	uint32 split = (uint32)(current * (evenWidth - 1) + 0.5);
392 	split += split & 1;
393 	uint32 last = (uint32)(peak * (evenWidth - 1) + 0.5);
394 	last += last & 1;
395 	uint32 over = overshot ? evenWidth : evenWidth + 1;
396 	over += over & 1;
397 
398 	for (uint32 x = 0; x < width; x += 2) {
399 		uint8 fadedB = (uint8)(((int)span[0] * kFadeFactor) >> 8);
400 		uint8 fadedG = (uint8)(((int)span[1] * kFadeFactor) >> 8);
401 		uint8 fadedR = (uint8)(((int)span[2] * kFadeFactor) >> 8);
402 		if (x < split) {
403 			span[0] = max_c(fillB, fadedB);
404 			span[1] = max_c(fillG, fadedG);
405 			span[2] = max_c(fillR, fadedR);
406 		} else if (x == split) {
407 			span[0] = currentB;
408 			span[1] = currentG;
409 			span[2] = currentR;
410 		} else if (x > split) {
411 			span[0] = max_c(emptyB, fadedB);
412 			span[1] = max_c(emptyG, fadedG);
413 			span[2] = max_c(emptyR, fadedR);
414 		}
415 		if (x == last) {
416 			span[0] = lastB;
417 			span[1] = lastG;
418 			span[2] = lastR;
419 		}
420 		if (x == over) {
421 			span[0] = overB;
422 			span[1] = overG;
423 			span[2] = overR;
424 		}
425 		span += 8;
426 	}
427 }
428 
429 
430 void
431 PeakView::_DrawBitmap()
432 {
433 	SetHighColor(0, 0, 0);
434 	BRect bitmapFrame = _BackBitmapFrame();
435 	BRect bitmapRect = fBackBitmap->Bounds();
436 	bitmapRect.bottom = bitmapRect.top;
437 	float channelHeight = (bitmapFrame.Height() + 1) / fChannelCount;
438 	for (uint32 i = 0; i < fChannelCount; i++) {
439 		BRect viewRect(bitmapFrame);
440 		viewRect.bottom = viewRect.top;
441 		viewRect.top += floorf(i * channelHeight + 0.5);
442 		if (i < fChannelCount - 1) {
443 			viewRect.bottom += floorf((i + 1) * channelHeight + 0.5) - 2;
444 			StrokeLine(BPoint(viewRect.left, viewRect.bottom + 1),
445 				BPoint(viewRect.right, viewRect.bottom + 1));
446 		} else
447 			viewRect.bottom += floorf((i + 1) * channelHeight + 0.5) - 1;
448 		DrawBitmapAsync(fBackBitmap, bitmapRect, viewRect);
449 		bitmapRect.OffsetBy(0, 1);
450 	}
451 }
452 
453 
454