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