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 125 / (fPlayFormat.u.raw_audio.format 126 & media_raw_audio_format::B_AUDIO_SIZE_MASK)]; 127 int64 frames = 0; 128 U sum = 0; 129 int64 sumCount = 0; 130 float middle = fHeight / 2; 131 int32 previewMax = 0; 132 //fMediaTrack->SeekToFrame(&frames); 133 134 TRACE("begin computing\n"); 135 136 int32 previewIndex = 0; 137 138 while (fIsRendering && fMediaTrack->ReadFrames(samples, &frames) == B_OK) { 139 //TRACE("reading block\n"); 140 int64 framesIndex = 0; 141 142 while (framesIndex < frames) { 143 for (; framesIndex < frames && sumCount < framesCount; 144 framesIndex++, sumCount++) { 145 sum += samples[2 * framesIndex]; 146 sum += samples[2 * framesIndex + 1]; 147 } 148 149 if (previewIndex >= SAMPLES_COUNT) 150 break; 151 152 if (sumCount >= framesCount) { 153 // TRACE("computing block %ld, sumCount %ld\n", previewIndex, 154 // sumCount); 155 fPreview[previewIndex] = (int32)(sum 156 / fPlayFormat.u.raw_audio.channel_count / framesCount); 157 if (previewMax < fPreview[previewIndex]) 158 previewMax = fPreview[previewIndex]; 159 sumCount = 0; 160 sum = 0; 161 previewIndex++; 162 } 163 } 164 } 165 166 if (previewMax <= 0) 167 return; 168 for (int i = 0; i < SAMPLES_COUNT; i++) 169 fPreview[i] = (int32)(fPreview[i] * 1.0 / previewMax 170 * middle + middle); 171 } 172 173 174 void 175 ScopeView::RenderLoop() 176 { 177 while (acquire_sem(fRenderSem) == B_OK) { 178 fIsRendering = true; 179 180 switch (fPlayFormat.u.raw_audio.format) { 181 case media_raw_audio_format::B_AUDIO_FLOAT: 182 ComputeRendering<float, float>(); 183 break; 184 case media_raw_audio_format::B_AUDIO_INT: 185 ComputeRendering<int32, int64>(); 186 break; 187 case media_raw_audio_format::B_AUDIO_SHORT: 188 ComputeRendering<int16, int64>(); 189 break; 190 case media_raw_audio_format::B_AUDIO_UCHAR: 191 ComputeRendering<uchar, uint32>(); 192 break; 193 case media_raw_audio_format::B_AUDIO_CHAR: 194 ComputeRendering<char, int32>(); 195 break; 196 } 197 198 TRACE("finished computing, rendering\n"); 199 200 /* rendering */ 201 RenderBitmap(); 202 203 TRACE("rendering done\n"); 204 205 /* ask drawing */ 206 207 fIsRendering = false; 208 209 if (Window()->LockWithTimeout(5000) == B_OK) { 210 Invalidate(); 211 TRACE("invalidate done\n"); 212 Window()->Unlock(); 213 } 214 } 215 } 216 217 218 void 219 ScopeView::SetMainTime(bigtime_t timestamp) 220 { 221 fMainTime = timestamp; 222 Invalidate(); 223 TRACE("invalidate done\n"); 224 } 225 226 227 void 228 ScopeView::SetTotalTime(bigtime_t timestamp, bool reset) 229 { 230 fTotalTime = timestamp; 231 if (reset) { 232 fMainTime = 0; 233 fLeftTime = 0; 234 fRightTime = fTotalTime; 235 } 236 Invalidate(); 237 TRACE("invalidate done\n"); 238 } 239 240 241 void 242 ScopeView::SetLeftTime(bigtime_t timestamp) 243 { 244 fLeftTime = timestamp; 245 RenderBitmap(); 246 Invalidate(); 247 TRACE("invalidate done\n"); 248 } 249 250 void 251 ScopeView::SetRightTime(bigtime_t timestamp) 252 { 253 fRightTime = timestamp; 254 RenderBitmap(); 255 Invalidate(); 256 TRACE("invalidate done\n"); 257 } 258 259 260 void 261 ScopeView::RenderTrack(BMediaTrack *track, const media_format &format) 262 { 263 fMediaTrack = track; 264 fPlayFormat = format; 265 release_sem(fRenderSem); 266 } 267 268 269 void 270 ScopeView::CancelRendering() 271 { 272 fIsRendering = false; 273 } 274 275 276 void 277 ScopeView::FrameResized(float width, float height) 278 { 279 InitBitmap(); 280 RenderBitmap(); 281 Invalidate(); 282 TRACE("invalidate done\n"); 283 } 284 285 286 void 287 ScopeView::MouseDown(BPoint position) 288 { 289 if (!fMediaTrack) 290 return; 291 292 uint32 buttons; 293 BPoint point; 294 GetMouse(&point, &buttons); 295 296 if (buttons & B_PRIMARY_MOUSE_BUTTON) { 297 // fill the drag message 298 BMessage drag(B_SIMPLE_DATA); 299 drag.AddInt32("be:actions", B_COPY_TARGET); 300 drag.AddString("be:clip_name", "Audio Clip"); 301 drag.AddString("be:types", B_FILE_MIME_TYPE); 302 303 uint8* data; 304 size_t size; 305 306 BMimeType wavType("audio/x-wav"); 307 if (wavType.InitCheck() < B_OK 308 || wavType.GetIcon(&data, &size) < B_OK) { 309 wavType.SetTo("audio"); 310 if (wavType.InitCheck() < B_OK 311 || wavType.GetIcon(&data, &size) < B_OK) { 312 return; 313 } 314 } 315 316 BBitmap* bitmap = new BBitmap( 317 BRect(0, 0, 31, 31), 0, B_RGBA32); 318 if (BIconUtils::GetVectorIcon(data, size, bitmap) < B_OK) { 319 delete[] data; 320 delete bitmap; 321 return; 322 } 323 delete[] data; 324 DragMessage(&drag, bitmap, B_OP_ALPHA, BPoint(0,0)); 325 } 326 } 327 328 329 void 330 ScopeView::InitBitmap() 331 { 332 if (fBitmap != NULL && fBitmapView != NULL) { 333 fBitmap->RemoveChild(fBitmapView); 334 delete fBitmapView; 335 } 336 if (fBitmap) 337 delete fBitmap; 338 339 BRect rect = Bounds(); 340 341 fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true); 342 memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); 343 344 rect.OffsetToSelf(B_ORIGIN); 345 fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView", 346 B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW); 347 fBitmap->AddChild(fBitmapView); 348 } 349 350 351 void 352 ScopeView::RenderBitmap() 353 { 354 if (!fMediaTrack) 355 return; 356 357 /* rendering */ 358 fBitmap->Lock(); 359 memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); 360 float width = fBitmapView->Bounds().Width() + 1; 361 362 fBitmapView->SetDrawingMode(B_OP_ADD); 363 fBitmapView->SetHighColor(15,60,15); 364 int32 leftIndex = 365 (fTotalTime != 0) ? fLeftTime * 20000 / fTotalTime : 0; 366 int32 rightIndex = 367 (fTotalTime != 0) ? fRightTime * 20000 / fTotalTime : 20000; 368 369 for (int32 i = leftIndex; i<rightIndex; i++) { 370 BPoint point((i - leftIndex) * width / (rightIndex - leftIndex), 371 fPreview[i]); 372 //TRACE("point x %f y %f\n", point.x, point.y); 373 fBitmapView->StrokeLine(point, point); 374 } 375 376 fBitmap->Unlock(); 377 } 378 379