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 #include <IconUtils.h> 12 #include <MimeType.h> 13 #include <Screen.h> 14 #include <Window.h> 15 #include "DrawingTidbits.h" 16 #include "ScopeView.h" 17 18 #define SAMPLES_COUNT 20000 19 20 //#define TRACE 1 21 #ifdef TRACE 22 #define TRACE(x...) printf(x) 23 #else 24 #define TRACE(x...) 25 #endif 26 27 ScopeView::ScopeView(BRect rect, uint32 resizeFlags) 28 : BView(rect, "scope", resizeFlags, B_WILL_DRAW | B_FRAME_EVENTS), 29 fThreadId(-1), 30 fBitmap(NULL), 31 fBitmapView(NULL), 32 fIsRendering(false), 33 fMediaTrack(NULL), 34 fMainTime(0), 35 fRightTime(1000000), 36 fLeftTime(0), 37 fTotalTime(1000000) 38 { 39 fHeight = Bounds().Height(); 40 } 41 42 43 ScopeView::~ScopeView() 44 { 45 delete_sem(fRenderSem); 46 } 47 48 49 void 50 ScopeView::AttachedToWindow() 51 { 52 SetViewColor(B_TRANSPARENT_COLOR); 53 InitBitmap(); 54 Run(); 55 } 56 57 58 void 59 ScopeView::DetachedFromWindow() 60 { 61 Quit(); 62 } 63 64 65 void 66 ScopeView::Draw(BRect updateRect) 67 { 68 BRect bounds = Bounds(); 69 SetHighColor(0,0,0); 70 71 if (!fIsRendering) 72 DrawBitmapAsync(fBitmap, BPoint(0, 0)); 73 else 74 FillRect(bounds); 75 76 float x = 0; 77 if (fTotalTime != 0) 78 x += (fMainTime - fLeftTime) * bounds.right 79 / (fRightTime - fLeftTime); 80 SetHighColor(60,255,40); 81 StrokeLine(BPoint(x, bounds.top), BPoint(x, bounds.bottom)); 82 83 Sync(); 84 } 85 86 87 void 88 ScopeView::Run() 89 { 90 fRenderSem = create_sem(0, "scope rendering"); 91 fThreadId = spawn_thread(&RenderLaunch, "Scope view", B_NORMAL_PRIORITY, 92 this); 93 if (fThreadId < 0) 94 return; 95 resume_thread(fThreadId); 96 } 97 98 void 99 ScopeView::Quit() 100 { 101 delete_sem(fRenderSem); 102 snooze(10000); 103 kill_thread(fThreadId); 104 } 105 106 107 108 int32 109 ScopeView::RenderLaunch(void *data) 110 { 111 ScopeView *scope = (ScopeView*) data; 112 scope->RenderLoop(); 113 return B_OK; 114 } 115 116 117 template<typename T, typename U> 118 void 119 ScopeView::ComputeRendering() 120 { 121 int64 framesCount = fMediaTrack->CountFrames() / SAMPLES_COUNT; 122 if (framesCount <= 0) 123 return; 124 T samples[fPlayFormat.u.raw_audio.buffer_size / (fPlayFormat.u.raw_audio.format 125 & media_raw_audio_format::B_AUDIO_SIZE_MASK)]; 126 int64 frames = 0; 127 U sum = 0; 128 int64 sumCount = 0; 129 float middle = fHeight / 2; 130 int32 previewMax = 0; 131 //fMediaTrack->SeekToFrame(&frames); 132 133 TRACE("begin computing\n"); 134 135 int32 previewIndex = 0; 136 137 while (fIsRendering && fMediaTrack->ReadFrames(samples, &frames) == B_OK) { 138 //TRACE("reading block\n"); 139 int64 framesIndex = 0; 140 141 while (framesIndex < frames) { 142 for (; framesIndex < frames && sumCount < framesCount; 143 framesIndex++, sumCount++) { 144 sum += samples[2 * framesIndex]; 145 sum += samples[2 * framesIndex + 1]; 146 } 147 148 if (previewIndex >= SAMPLES_COUNT) 149 break; 150 151 if (sumCount >= framesCount) { 152 //TRACE("computing block %ld, sumCount %ld\n", previewIndex, sumCount); 153 fPreview[previewIndex] = (int32)(sum 154 / fPlayFormat.u.raw_audio.channel_count / framesCount); 155 if (previewMax < fPreview[previewIndex]) 156 previewMax = fPreview[previewIndex]; 157 sumCount = 0; 158 sum = 0; 159 previewIndex++; 160 } 161 } 162 } 163 164 if (previewMax <= 0) 165 return; 166 for (int i = 0; i < SAMPLES_COUNT; i++) 167 fPreview[i] = (int32)(fPreview[i] * 1.0 / previewMax * middle + middle); 168 } 169 170 171 void 172 ScopeView::RenderLoop() 173 { 174 while (acquire_sem(fRenderSem) == B_OK) { 175 fIsRendering = true; 176 177 switch (fPlayFormat.u.raw_audio.format) { 178 case media_raw_audio_format::B_AUDIO_FLOAT: 179 ComputeRendering<float, float>(); 180 break; 181 case media_raw_audio_format::B_AUDIO_INT: 182 ComputeRendering<int32, int64>(); 183 break; 184 case media_raw_audio_format::B_AUDIO_SHORT: 185 ComputeRendering<int16, int64>(); 186 break; 187 case media_raw_audio_format::B_AUDIO_UCHAR: 188 ComputeRendering<uchar, uint32>(); 189 break; 190 case media_raw_audio_format::B_AUDIO_CHAR: 191 ComputeRendering<char, int32>(); 192 break; 193 } 194 195 TRACE("finished computing, rendering\n"); 196 197 /* rendering */ 198 RenderBitmap(); 199 200 TRACE("rendering done\n"); 201 202 /* ask drawing */ 203 204 fIsRendering = false; 205 206 if (Window()->LockWithTimeout(5000) == B_OK) { 207 Invalidate(); 208 TRACE("invalidate done\n"); 209 Window()->Unlock(); 210 } 211 } 212 } 213 214 215 void 216 ScopeView::SetMainTime(bigtime_t timestamp) 217 { 218 fMainTime = timestamp; 219 Invalidate(); 220 TRACE("invalidate done\n"); 221 } 222 223 224 void 225 ScopeView::SetTotalTime(bigtime_t timestamp, bool reset) 226 { 227 fTotalTime = timestamp; 228 if (reset) { 229 fMainTime = 0; 230 fLeftTime = 0; 231 fRightTime = fTotalTime; 232 } 233 Invalidate(); 234 TRACE("invalidate done\n"); 235 } 236 237 238 void 239 ScopeView::SetLeftTime(bigtime_t timestamp) 240 { 241 fLeftTime = timestamp; 242 RenderBitmap(); 243 Invalidate(); 244 TRACE("invalidate done\n"); 245 } 246 247 void 248 ScopeView::SetRightTime(bigtime_t timestamp) 249 { 250 fRightTime = timestamp; 251 RenderBitmap(); 252 Invalidate(); 253 TRACE("invalidate done\n"); 254 } 255 256 257 void 258 ScopeView::RenderTrack(BMediaTrack *track, media_format format) 259 { 260 fMediaTrack = track; 261 fPlayFormat = format; 262 release_sem(fRenderSem); 263 } 264 265 266 void 267 ScopeView::CancelRendering() 268 { 269 fIsRendering = false; 270 } 271 272 273 void 274 ScopeView::FrameResized(float width, float height) 275 { 276 InitBitmap(); 277 RenderBitmap(); 278 Invalidate(); 279 TRACE("invalidate done\n"); 280 } 281 282 283 void 284 ScopeView::MouseDown(BPoint position) 285 { 286 if (!fMediaTrack) 287 return; 288 289 uint32 buttons; 290 BPoint point; 291 GetMouse(&point, &buttons); 292 293 if (buttons & B_PRIMARY_MOUSE_BUTTON) { 294 // fill the drag message 295 BMessage drag(B_SIMPLE_DATA); 296 drag.AddInt32("be:actions", B_COPY_TARGET); 297 drag.AddString("be:clip_name", "Audio Clip"); 298 drag.AddString("be:types", B_FILE_MIME_TYPE); 299 300 uint8* data; 301 size_t size; 302 303 BMimeType wavType("audio/x-wav"); 304 if (wavType.InitCheck() < B_OK 305 || wavType.GetIcon(&data, &size) < B_OK) { 306 wavType.SetTo("audio"); 307 if (wavType.InitCheck() < B_OK 308 || wavType.GetIcon(&data, &size) < B_OK) { 309 return; 310 } 311 } 312 313 BBitmap* bitmap = new BBitmap( 314 BRect(0, 0, 31, 31), 0, B_RGBA32); 315 if (BIconUtils::GetVectorIcon(data, size, bitmap) < B_OK) { 316 delete[] data; 317 delete bitmap; 318 return; 319 } 320 delete[] data; 321 DragMessage(&drag, bitmap, B_OP_ALPHA, BPoint(0,0)); 322 } 323 } 324 325 326 void 327 ScopeView::InitBitmap() 328 { 329 if (fBitmapView) { 330 fBitmap->RemoveChild(fBitmapView); 331 delete fBitmapView; 332 } 333 if (fBitmap) 334 delete fBitmap; 335 336 BRect rect = Bounds(); 337 338 fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true); 339 memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); 340 341 rect.OffsetToSelf(B_ORIGIN); 342 fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView", 343 B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW); 344 fBitmap->AddChild(fBitmapView); 345 } 346 347 348 void 349 ScopeView::RenderBitmap() 350 { 351 if (!fMediaTrack) 352 return; 353 354 /* rendering */ 355 fBitmap->Lock(); 356 memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); 357 float width = fBitmapView->Bounds().Width() + 1; 358 359 fBitmapView->SetDrawingMode(B_OP_ADD); 360 fBitmapView->SetHighColor(15,60,15); 361 int32 leftIndex = 362 (fTotalTime != 0) ? fLeftTime * 20000 / fTotalTime : 0; 363 int32 rightIndex = 364 (fTotalTime != 0) ? fRightTime * 20000 / fTotalTime : 20000; 365 366 for (int32 i = leftIndex; i<rightIndex; i++) { 367 BPoint point((i - leftIndex) * width / (rightIndex - leftIndex), 368 fPreview[i]); 369 //TRACE("point x %f y %f\n", point.x, point.y); 370 fBitmapView->StrokeLine(point, point); 371 } 372 373 fBitmap->Unlock(); 374 } 375 376