1 /* 2 * Copyright 2005, Jérôme Duval. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Inspired by SoundCapture from Be newsletter (Media Kit Basics: 6 * Consumers and Producers) 7 */ 8 9 #include <stdio.h> 10 #include <string.h> 11 12 #include <MediaDefs.h> 13 #include <Screen.h> 14 #include <Window.h> 15 16 #include "DrawingTidbits.h" 17 #include "VUView.h" 18 19 const rgb_color back_color = {12, 36, 12}; 20 const rgb_color low_color = {40, 120, 40}; 21 const rgb_color high_color = {240, 255, 240}; 22 23 VUView::VUView(BRect rect, uint32 resizeFlags) 24 : BView(rect, "vumeter", resizeFlags, B_WILL_DRAW), 25 fThreadId(-1), 26 fBitmap(NULL), 27 fQuitting(false) 28 { 29 rect.OffsetTo(B_ORIGIN); 30 fLevelCount = int(rect.Height()) / 2; 31 fChannels = 2; 32 fCurrentLevels = new int32[fChannels]; 33 for (int channel = 0; channel < fChannels; channel++) 34 fCurrentLevels[channel] = 0; 35 fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true); 36 37 38 memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); 39 40 fBitmapView = new BView(rect, "bitmapView", B_FOLLOW_LEFT|B_FOLLOW_TOP, 41 B_WILL_DRAW); 42 fBitmap->AddChild(fBitmapView); 43 } 44 45 46 VUView::~VUView() 47 { 48 delete fBitmap; 49 } 50 51 52 void 53 VUView::AttachedToWindow() 54 { 55 SetViewColor(B_TRANSPARENT_COLOR); 56 _Run(); 57 } 58 59 60 void 61 VUView::DetachedFromWindow() 62 { 63 _Quit(); 64 } 65 66 67 void 68 VUView::Draw(BRect updateRect) 69 { 70 DrawBitmap(fBitmap); 71 72 Sync(); 73 } 74 75 76 void 77 VUView::_Run() 78 { 79 fThreadId = spawn_thread(_RenderLaunch, "VU view", B_NORMAL_PRIORITY, 80 this); 81 if (fThreadId < 0) 82 return; 83 resume_thread(fThreadId); 84 } 85 86 void 87 VUView::_Quit() 88 { 89 fQuitting = true; 90 snooze(10000); 91 kill_thread(fThreadId); 92 } 93 94 95 96 int32 97 VUView::_RenderLaunch(void *data) 98 { 99 VUView *vu = (VUView*) data; 100 vu->_RenderLoop(); 101 return B_OK; 102 } 103 104 105 #define SHIFT_UNTIL(value,shift,min) \ 106 value = (value - shift > min) ? (value - shift) : min 107 108 void 109 VUView::_RenderLoop() 110 { 111 rgb_color levels[fLevelCount][2]; 112 113 for (int32 i = 0; i < fLevelCount; i++) { 114 levels[i][0] = levels[i][1] = back_color; 115 } 116 117 while (!fQuitting) { 118 119 /* computing */ 120 for (int32 channel = 0; channel < 2; channel++) { 121 int32 level = fCurrentLevels[channel]; 122 for (int32 i = 0; i < level; i++) { 123 if (levels[i][channel].red >= 90) { 124 SHIFT_UNTIL(levels[i][channel].red, 15, low_color.red); 125 SHIFT_UNTIL(levels[i][channel].blue, 15, low_color.blue); 126 } else { 127 SHIFT_UNTIL(levels[i][channel].red, 7, low_color.red); 128 SHIFT_UNTIL(levels[i][channel].blue, 7, low_color.blue); 129 SHIFT_UNTIL(levels[i][channel].green, 14, low_color.green); 130 } 131 } 132 133 levels[level][channel] = high_color; 134 135 for (int32 i = level + 1; i < fLevelCount; i++) { 136 if (levels[i][channel].red >= 85) { 137 SHIFT_UNTIL(levels[i][channel].red, 15, back_color.red); 138 SHIFT_UNTIL(levels[i][channel].blue, 15, back_color.blue); 139 } else { 140 SHIFT_UNTIL(levels[i][channel].red, 7, back_color.red); 141 SHIFT_UNTIL(levels[i][channel].blue, 7, back_color.blue); 142 SHIFT_UNTIL(levels[i][channel].green, 14, 143 back_color.green); 144 } 145 } 146 } 147 148 /* rendering */ 149 fBitmap->Lock(); 150 fBitmapView->BeginLineArray(fLevelCount * 2); 151 BPoint start1, end1, start2, end2; 152 start1.x = 3; 153 start2.x = 22; 154 end1.x = 16; 155 end2.x = 35; 156 start1.y = end1.y = start2.y = end2.y = 2; 157 for (int32 i = fLevelCount - 1; i >= 0; i--) { 158 fBitmapView->AddLine(start1, end1, levels[i][0]); 159 fBitmapView->AddLine(start2, end2, levels[i][1]); 160 start1.y = end1.y = start2.y = end2.y = end2.y + 2; 161 } 162 fBitmapView->EndLineArray(); 163 fBitmap->Unlock(); 164 165 /* ask drawing */ 166 167 if (Window()->LockWithTimeout(5000) == B_OK) { 168 Invalidate(); 169 Window()->Unlock(); 170 snooze(50000); 171 } 172 } 173 } 174 175 176 template<typename T> 177 T 178 VUView::_ComputeNextLevel(const void *data, size_t size, uint32 format, 179 int32 channel) 180 { 181 const T* samp = (const T*)data; 182 183 // get the min and max values in the nibbling interval 184 // and set max to be the greater of the absolute value 185 // of these. 186 187 T min = 0, max = 0; 188 for (uint32 i = channel; i < size/sizeof(T); i += fChannels) { 189 if (min > samp[i]) 190 min = samp[i]; 191 else if (max < samp[i]) 192 max = samp[i]; 193 } 194 if (-max > (min + 1)) 195 max = -min; 196 197 return max; 198 } 199 200 201 void 202 VUView::ComputeLevels(const void* data, size_t size, uint32 format) 203 { 204 for (int32 channel = 0; channel < fChannels; channel++) { 205 switch (format) { 206 case media_raw_audio_format::B_AUDIO_FLOAT: 207 { 208 float max = _ComputeNextLevel<float>(data, size, format, 209 channel); 210 fCurrentLevels[channel] = (uint8)(max * 127); 211 break; 212 } 213 case media_raw_audio_format::B_AUDIO_INT: 214 { 215 int32 max = _ComputeNextLevel<int32>(data, size, format, 216 channel); 217 fCurrentLevels[channel] = max / (2 << (32-7)); 218 break; 219 } 220 case media_raw_audio_format::B_AUDIO_SHORT: 221 { 222 int16 max = _ComputeNextLevel<int16>(data, size, format, 223 channel); 224 fCurrentLevels[channel] = max / (2 << (16-7)); 225 break; 226 } 227 case media_raw_audio_format::B_AUDIO_UCHAR: 228 { 229 uchar max = _ComputeNextLevel<uchar>(data, size, format, 230 channel); 231 fCurrentLevels[channel] = max / 2 - 127; 232 break; 233 } 234 case media_raw_audio_format::B_AUDIO_CHAR: 235 { 236 char max = _ComputeNextLevel<char>(data, size, format, 237 channel); 238 fCurrentLevels[channel] = max / 2; 239 break; 240 } 241 } 242 if (fCurrentLevels[channel] < 0) 243 fCurrentLevels[channel] = 0; 244 } 245 } 246