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