/*
* Copyright 2005, Jérôme Duval. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Inspired by SoundCapture from Be newsletter (Media Kit Basics:
* Consumers and Producers)
*/
#include <stdio.h>
#include <string.h>
#include <IconUtils.h>
#include <MimeType.h>
#include <Screen.h>
#include <Window.h>
#include "DrawingTidbits.h"
#include "ScopeView.h"
#define SAMPLES_COUNT 20000
//#define TRACE 1
#ifdef TRACE
#define TRACE(x...) printf(x)
#else
#define TRACE(x...)
#endif
ScopeView::ScopeView(BRect rect, uint32 resizeFlags)
: BView(rect, "scope", resizeFlags, B_WILL_DRAW | B_FRAME_EVENTS),
fThreadId(-1),
fBitmap(NULL),
fBitmapView(NULL),
fIsRendering(false),
fMediaTrack(NULL),
fMainTime(0),
fRightTime(1000000),
fLeftTime(0),
fTotalTime(1000000)
{
fHeight = Bounds().Height();
}
ScopeView::~ScopeView()
{
delete_sem(fRenderSem);
}
void
ScopeView::AttachedToWindow()
{
SetViewColor(B_TRANSPARENT_COLOR);
InitBitmap();
Run();
}
void
ScopeView::DetachedFromWindow()
{
Quit();
}
void
ScopeView::Draw(BRect updateRect)
{
BRect bounds = Bounds();
SetHighColor(0,0,0);
if (!fIsRendering)
DrawBitmapAsync(fBitmap, BPoint(0, 0));
else
FillRect(bounds);
float x = 0;
if (fTotalTime != 0)
x += (fMainTime - fLeftTime) * bounds.right
/ (fRightTime - fLeftTime);
SetHighColor(60,255,40);
StrokeLine(BPoint(x, bounds.top), BPoint(x, bounds.bottom));
Sync();
}
void
ScopeView::Run()
{
fRenderSem = create_sem(0, "scope rendering");
fThreadId = spawn_thread(&RenderLaunch, "Scope view", B_NORMAL_PRIORITY,
this);
if (fThreadId < 0)
return;
resume_thread(fThreadId);
}
void
ScopeView::Quit()
{
delete_sem(fRenderSem);
snooze(10000);
kill_thread(fThreadId);
}
int32
ScopeView::RenderLaunch(void *data)
{
ScopeView *scope = (ScopeView*) data;
scope->RenderLoop();
return B_OK;
}
template<typename T, typename U>
void
ScopeView::ComputeRendering()
{
int64 framesCount = fMediaTrack->CountFrames() / SAMPLES_COUNT;
if (framesCount <= 0)
return;
T samples[fPlayFormat.u.raw_audio.buffer_size
/ (fPlayFormat.u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK)];
int64 frames = 0;
U sum = 0;
int64 sumCount = 0;
float middle = fHeight / 2;
int32 previewMax = 0;
//fMediaTrack->SeekToFrame(&frames);
TRACE("begin computing\n");
int32 previewIndex = 0;
while (fIsRendering && fMediaTrack->ReadFrames(samples, &frames) == B_OK) {
//TRACE("reading block\n");
int64 framesIndex = 0;
while (framesIndex < frames) {
for (; framesIndex < frames && sumCount < framesCount;
framesIndex++, sumCount++) {
sum += samples[2 * framesIndex];
sum += samples[2 * framesIndex + 1];
}
if (previewIndex >= SAMPLES_COUNT)
break;
if (sumCount >= framesCount) {
// TRACE("computing block %ld, sumCount %ld\n", previewIndex,
// sumCount);
fPreview[previewIndex] = (int32)(sum
/ fPlayFormat.u.raw_audio.channel_count / framesCount);
if (previewMax < fPreview[previewIndex])
previewMax = fPreview[previewIndex];
sumCount = 0;
sum = 0;
previewIndex++;
}
}
}
if (previewMax <= 0)
return;
for (int i = 0; i < SAMPLES_COUNT; i++)
fPreview[i] = (int32)(fPreview[i] * 1.0 / previewMax
* middle + middle);
}
void
ScopeView::RenderLoop()
{
while (acquire_sem(fRenderSem) == B_OK) {
fIsRendering = true;
switch (fPlayFormat.u.raw_audio.format) {
case media_raw_audio_format::B_AUDIO_FLOAT:
ComputeRendering<float, float>();
break;
case media_raw_audio_format::B_AUDIO_INT:
ComputeRendering<int32, int64>();
break;
case media_raw_audio_format::B_AUDIO_SHORT:
ComputeRendering<int16, int64>();
break;
case media_raw_audio_format::B_AUDIO_UCHAR:
ComputeRendering<uchar, uint32>();
break;
case media_raw_audio_format::B_AUDIO_CHAR:
ComputeRendering<char, int32>();
break;
}
TRACE("finished computing, rendering\n");
/* rendering */
RenderBitmap();
TRACE("rendering done\n");
/* ask drawing */
fIsRendering = false;
if (Window()->LockWithTimeout(5000) == B_OK) {
Invalidate();
TRACE("invalidate done\n");
Window()->Unlock();
}
}
}
void
ScopeView::SetMainTime(bigtime_t timestamp)
{
fMainTime = timestamp;
Invalidate();
TRACE("invalidate done\n");
}
void
ScopeView::SetTotalTime(bigtime_t timestamp, bool reset)
{
fTotalTime = timestamp;
if (reset) {
fMainTime = 0;
fLeftTime = 0;
fRightTime = fTotalTime;
}
Invalidate();
TRACE("invalidate done\n");
}
void
ScopeView::SetLeftTime(bigtime_t timestamp)
{
fLeftTime = timestamp;
RenderBitmap();
Invalidate();
TRACE("invalidate done\n");
}
void
ScopeView::SetRightTime(bigtime_t timestamp)
{
fRightTime = timestamp;
RenderBitmap();
Invalidate();
TRACE("invalidate done\n");
}
void
ScopeView::RenderTrack(BMediaTrack *track, const media_format &format)
{
fMediaTrack = track;
fPlayFormat = format;
release_sem(fRenderSem);
}
void
ScopeView::CancelRendering()
{
fIsRendering = false;
}
void
ScopeView::FrameResized(float width, float height)
{
InitBitmap();
RenderBitmap();
Invalidate();
TRACE("invalidate done\n");
}
void
ScopeView::MouseDown(BPoint position)
{
if (!fMediaTrack)
return;
uint32 buttons;
BPoint point;
GetMouse(&point, &buttons);
if (buttons & B_PRIMARY_MOUSE_BUTTON) {
// fill the drag message
BMessage drag(B_SIMPLE_DATA);
drag.AddInt32("be:actions", B_COPY_TARGET);
drag.AddString("be:clip_name", "Audio Clip");
drag.AddString("be:types", B_FILE_MIME_TYPE);
uint8* data;
size_t size;
BMimeType wavType("audio/x-wav");
if (wavType.InitCheck() < B_OK
|| wavType.GetIcon(&data, &size) < B_OK) {
wavType.SetTo("audio");
if (wavType.InitCheck() < B_OK
|| wavType.GetIcon(&data, &size) < B_OK) {
return;
}
}
BBitmap* bitmap = new BBitmap(
BRect(0, 0, 31, 31), 0, B_RGBA32);
if (BIconUtils::GetVectorIcon(data, size, bitmap) < B_OK) {
delete[] data;
delete bitmap;
return;
}
delete[] data;
DragMessage(&drag, bitmap, B_OP_ALPHA, BPoint(0,0));
}
}
void
ScopeView::InitBitmap()
{
if (fBitmapView) {
fBitmap->RemoveChild(fBitmapView);
delete fBitmapView;
}
if (fBitmap)
delete fBitmap;
BRect rect = Bounds();
fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true);
memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
rect.OffsetToSelf(B_ORIGIN);
fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView",
B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW);
fBitmap->AddChild(fBitmapView);
}
void
ScopeView::RenderBitmap()
{
if (!fMediaTrack)
return;
/* rendering */
fBitmap->Lock();
memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
float width = fBitmapView->Bounds().Width() + 1;
fBitmapView->SetDrawingMode(B_OP_ADD);
fBitmapView->SetHighColor(15,60,15);
int32 leftIndex =
(fTotalTime != 0) ? fLeftTime * 20000 / fTotalTime : 0;
int32 rightIndex =
(fTotalTime != 0) ? fRightTime * 20000 / fTotalTime : 20000;
for (int32 i = leftIndex; i<rightIndex; i++) {
BPoint point((i - leftIndex) * width / (rightIndex - leftIndex),
fPreview[i]);
//TRACE("point x %f y %f\n", point.x, point.y);
fBitmapView->StrokeLine(point, point);
}
fBitmap->Unlock();
}
↑ V595 The 'fBitmap' pointer was utilized before it was verified against nullptr. Check lines: 333, 336.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fRenderSem, fPreview.