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