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: Consumers and Producers) 6 */ 7 8 #include <stdio.h> 9 #include <string.h> 10 #include <Screen.h> 11 #include <Window.h> 12 #include "DrawingTidbits.h" 13 #include "ScopeView.h" 14 15 //#define TRACE 1 16 #ifdef TRACE 17 #define TRACE(x) printf(x) 18 #else 19 #define TRACE(x) 20 #endif 21 22 ScopeView::ScopeView(BRect rect, uint32 resizeFlags) 23 : BView(rect, "scope", resizeFlags, B_WILL_DRAW | B_FRAME_EVENTS), 24 fThreadId(-1), 25 fBitmap(NULL), 26 fIsRendering(false), 27 fMediaTrack(NULL), 28 fQuitting(false), 29 fMainTime(0), 30 fRightTime(1000000), 31 fLeftTime(0), 32 fTotalTime(1000000) 33 { 34 fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true); 35 memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); 36 37 rect.OffsetToSelf(B_ORIGIN); 38 rect.right -= 2; 39 fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView", B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW); 40 fBitmap->AddChild(fBitmapView); 41 42 fRenderSem = create_sem(0, "scope rendering"); 43 fHeight = Bounds().Height(); 44 } 45 46 47 ScopeView::~ScopeView() 48 { 49 delete_sem(fRenderSem); 50 } 51 52 53 void 54 ScopeView::AttachedToWindow() 55 { 56 SetViewColor(B_TRANSPARENT_COLOR); 57 Run(); 58 } 59 60 61 void 62 ScopeView::DetachedFromWindow() 63 { 64 Quit(); 65 } 66 67 68 void 69 ScopeView::Draw(BRect updateRect) 70 { 71 BRect bounds = Bounds(); 72 if (!fIsRendering) 73 DrawBitmapAsync(fBitmap, BPoint(2,0)); 74 75 float x = 2; 76 if (fTotalTime !=0) 77 x = 2 + (fMainTime - fLeftTime) * (bounds.right - 2) / (fRightTime - fLeftTime); 78 SetHighColor(60,255,40); 79 StrokeLine(BPoint(x, bounds.top), BPoint(x, bounds.bottom)); 80 81 Sync(); 82 } 83 84 85 void 86 ScopeView::Run() 87 { 88 fThreadId = spawn_thread(&RenderLaunch, "Scope view", B_NORMAL_PRIORITY, this); 89 if (fThreadId < 0) 90 return; 91 resume_thread(fThreadId); 92 } 93 94 void 95 ScopeView::Quit() 96 { 97 delete_sem(fRenderSem); 98 fQuitting = true; 99 snooze(10000); 100 kill_thread(fThreadId); 101 } 102 103 104 105 int32 106 ScopeView::RenderLaunch(void *data) 107 { 108 ScopeView *scope = (ScopeView*) data; 109 scope->RenderLoop(); 110 return B_OK; 111 } 112 113 114 void 115 ScopeView::RenderLoop() 116 { 117 118 while (!fQuitting) { 119 120 if (acquire_sem(fRenderSem)!=B_OK) 121 continue; 122 123 fIsRendering = true; 124 125 int32 frame_size = (fPlayFormat.u.raw_audio.format & 0xf) * fPlayFormat.u.raw_audio.channel_count; 126 int64 totalFrames = fMediaTrack->CountFrames(); 127 int16 samples[fPlayFormat.u.raw_audio.buffer_size / (fPlayFormat.u.raw_audio.format & 0xf)]; 128 int64 frames = 0; 129 int64 sum = 0; 130 int64 framesIndex = 0; 131 int32 sumCount = 0; 132 fMediaTrack->SeekToFrame(&frames); 133 134 TRACE("begin computing\n"); 135 136 int32 previewIndex = 0; 137 138 while (fMediaTrack->ReadFrames(samples, &frames) == B_OK) { 139 //TRACE("reading block\n"); 140 framesIndex = 0; 141 142 while (framesIndex < frames) { 143 144 for (; framesIndex < frames && sumCount < totalFrames/20000; framesIndex++, sumCount++) { 145 sum += samples[2*framesIndex]; 146 sum += samples[2*framesIndex+1]; 147 } 148 149 if (previewIndex >= 20000) { 150 break; 151 } 152 153 if (sumCount >= totalFrames/20000) { 154 //TRACE("computing block %ld, sumCount %ld\n", previewIndex, sumCount); 155 fPreview[previewIndex++] = (int32)(sum / 2 /(totalFrames/20000) / 32767.0 * fHeight / 2 + fHeight / 2); 156 sumCount = 0; 157 sum = 0; 158 } 159 } 160 161 162 } 163 164 TRACE("finished computing\n"); 165 166 /* rendering */ 167 RenderBitmap(); 168 169 /* ask drawing */ 170 171 fIsRendering = false; 172 173 if (Window()->LockWithTimeout(5000) == B_OK) { 174 Invalidate(); 175 Window()->Unlock(); 176 } 177 } 178 } 179 180 181 void 182 ScopeView::SetMainTime(bigtime_t timestamp) 183 { 184 fMainTime = timestamp; 185 Invalidate(); 186 } 187 188 189 void 190 ScopeView::SetTotalTime(bigtime_t timestamp) 191 { 192 fTotalTime = timestamp; 193 Invalidate(); 194 } 195 196 void 197 ScopeView::SetLeftTime(bigtime_t timestamp) 198 { 199 fLeftTime = timestamp; 200 RenderBitmap(); 201 Invalidate(); 202 } 203 204 void 205 ScopeView::SetRightTime(bigtime_t timestamp) 206 { 207 fRightTime = timestamp; 208 RenderBitmap(); 209 Invalidate(); 210 } 211 212 213 void 214 ScopeView::RenderTrack(BMediaTrack *track, media_format format) 215 { 216 fMediaTrack = track; 217 fPlayFormat = format; 218 release_sem(fRenderSem); 219 } 220 221 222 void 223 ScopeView::FrameResized(float width, float height) 224 { 225 InitBitmap(); 226 RenderBitmap(); 227 Invalidate(); 228 } 229 230 231 void 232 ScopeView::InitBitmap() 233 { 234 if (fBitmapView) { 235 fBitmap->RemoveChild(fBitmapView); 236 delete fBitmapView; 237 } 238 if (fBitmap) 239 delete fBitmap; 240 241 BRect rect = Bounds(); 242 243 fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true); 244 memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); 245 246 rect.OffsetToSelf(B_ORIGIN); 247 rect.right -= 2; 248 fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView", B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW); 249 fBitmap->AddChild(fBitmapView); 250 } 251 252 253 void 254 ScopeView::RenderBitmap() 255 { 256 if (!fMediaTrack) 257 return; 258 259 /* rendering */ 260 fBitmap->Lock(); 261 memset(fBitmap->Bits(), 0, fBitmap->BitsLength()); 262 float width = fBitmapView->Bounds().Width(); 263 264 fBitmapView->SetDrawingMode(B_OP_ADD); 265 fBitmapView->SetHighColor(15,60,15); 266 int32 leftIndex = (fTotalTime != 0) ? fLeftTime * 20000 / fTotalTime : 0; 267 int32 rightIndex = (fTotalTime != 0) ? fRightTime * 20000 / fTotalTime : 20000; 268 269 for (int32 i = leftIndex; i<rightIndex; i++) { 270 BPoint point((i - leftIndex) * width / (rightIndex - leftIndex), fPreview[i]); 271 //TRACE("point x %f y %f\n", point.x, point.y); 272 fBitmapView->StrokeLine(point, point); 273 } 274 275 fBitmap->Unlock(); 276 } 277 278