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 27ScopeView::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 43ScopeView::~ScopeView() 44{ 45 delete_sem(fRenderSem); 46} 47 48 49void 50ScopeView::AttachedToWindow() 51{ 52 SetViewColor(B_TRANSPARENT_COLOR); 53 InitBitmap(); 54 Run(); 55} 56 57 58void 59ScopeView::DetachedFromWindow() 60{ 61 Quit(); 62} 63 64 65void 66ScopeView::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 87void 88ScopeView::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 98void 99ScopeView::Quit() 100{ 101 delete_sem(fRenderSem); 102 snooze(10000); 103 kill_thread(fThreadId); 104} 105 106 107 108int32 109ScopeView::RenderLaunch(void *data) 110{ 111 ScopeView *scope = (ScopeView*) data; 112 scope->RenderLoop(); 113 return B_OK; 114} 115 116 117template<typename T, typename U> 118void 119ScopeView::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 174void 175ScopeView::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 218void 219ScopeView::SetMainTime(bigtime_t timestamp) 220{ 221 fMainTime = timestamp; 222 Invalidate(); 223 TRACE("invalidate done\n"); 224} 225 226 227void 228ScopeView::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 241void 242ScopeView::SetLeftTime(bigtime_t timestamp) 243{ 244 fLeftTime = timestamp; 245 RenderBitmap(); 246 Invalidate(); 247 TRACE("invalidate done\n"); 248} 249 250void 251ScopeView::SetRightTime(bigtime_t timestamp) 252{ 253 fRightTime = timestamp; 254 RenderBitmap(); 255 Invalidate(); 256 TRACE("invalidate done\n"); 257} 258 259 260void 261ScopeView::RenderTrack(BMediaTrack *track, const media_format &format) 262{ 263 fMediaTrack = track; 264 fPlayFormat = format; 265 release_sem(fRenderSem); 266} 267 268 269void 270ScopeView::CancelRendering() 271{ 272 fIsRendering = false; 273} 274 275 276void 277ScopeView::FrameResized(float width, float height) 278{ 279 InitBitmap(); 280 RenderBitmap(); 281 Invalidate(); 282 TRACE("invalidate done\n"); 283} 284 285 286void 287ScopeView::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 329void 330ScopeView::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 351void 352ScopeView::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