* The SeekSlider did not update the knob when it was resized.

* Subtile visual improvements to the SeekSlider.
* Added a PeakView for displaying the audio peaks that are produced by
  the AudioProducer.
* A MessageEvent can now directly take a custom BMessage for delivery.
* The peak notification mechanism is a bit separate from the rest of the
  Controller notification design, since the notification delivery should
  be delayed until the audio is actually audible. I may change this
  quick and dirty design though, since it is not so nice. The target
  time could also be part of the message and be handled at a different
  stage, but that would make it less efficient.
* Layout improvements to the playback controls.
* Code cleanup here and there, changed some license statements.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26280 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2008-07-06 18:29:42 +00:00
parent e146a7bf2f
commit 9282400ff4
15 changed files with 738 additions and 115 deletions

View File

@ -32,7 +32,7 @@
ControllerView::ControllerView(BRect frame, Controller* controller,
Playlist* playlist)
: TransportControlGroup(frame)
: TransportControlGroup(frame, true, true, false)
, fController(controller)
, fPlaylist(playlist)
, fPlaylistObserver(new PlaylistObserver(this))

View File

@ -26,6 +26,7 @@ for sourceDir in $(sourceDirs) {
Application MediaPlayer :
# interface
DrawingTidbits.cpp
PeakView.cpp
SeekSlider.cpp
TransportButton.cpp
VolumeSlider.cpp

View File

@ -41,8 +41,10 @@
#include <String.h>
#include <View.h>
#include "AudioProducer.h"
#include "ControllerObserver.h"
#include "MainApp.h"
#include "PeakView.h"
#include "PlaylistObserver.h"
#include "PlaylistWindow.h"
#include "SettingsWindow.h"
@ -170,6 +172,9 @@ MainWin::MainWin()
fPlaylist->AddListener(fPlaylistObserver);
fController->SetVideoView(fVideoView);
fController->AddListener(fControllerObserver);
PeakView* peakView = fControls->GetPeakView();
peakView->SetPeakNotificationWhat(MSG_PEAK_NOTIFICATION);
fController->SetPeakListener(peakView);
// printf("fMenuBarHeight %d\n", fMenuBarHeight);
// printf("fControlsHeight %d\n", fControlsHeight);
@ -201,6 +206,7 @@ MainWin::~MainWin()
fPlaylist->RemoveListener(fPlaylistObserver);
fController->RemoveListener(fControllerObserver);
fController->SetPeakListener(NULL);
// give the views a chance to detach from any notifiers
// before we delete them

View File

@ -1,13 +1,10 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
// NOTE: Based on my code in the BeOS interface for the VLC media player
// that I did during the VLC 0.4.3 - 0.4.6 times. Code not done by me
// that I did during the VLC 0.4.3 - 0.4.6 times. Code not written by me
// removed. -Stephan Aßmus
#include "TransportControlGroup.h"
@ -18,6 +15,7 @@
#include <String.h>
#include "ButtonBitmaps.h"
#include "PeakView.h"
#include "PlaybackState.h"
#include "SeekSlider.h"
#include "TransportButton.h"
@ -53,63 +51,55 @@ enum {
// constructor
TransportControlGroup::TransportControlGroup(BRect frame, bool useSkipButtons,
bool useWindButtons)
bool usePeakView, bool useWindButtons)
: BView(frame, "transport control group",
B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
B_WILL_DRAW | B_FRAME_EVENTS)
, fBottomControlHeight(0.0)
, fPeakViewMinWidth(0.0)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
frame.Set(0.0, 0.0, 10.0, 10.0);
// Seek Slider
fSeekSlider = new SeekSlider(frame, "seek slider",
new BMessage(MSG_SEEK),
0, kPositionFactor);
fSeekSlider = new SeekSlider(frame, "seek slider", new BMessage(MSG_SEEK),
0, kPositionFactor);
fSeekSlider->ResizeToPreferred();
AddChild(fSeekSlider);
// Buttons
if (useSkipButtons) {
// Skip Back
frame.right = kRewindBitmapWidth - 1;
frame.bottom = kRewindBitmapHeight - 1;
if (useSkipButtons) {
// Skip Back
frame.right = kRewindBitmapWidth - 1;
frame.bottom = kRewindBitmapHeight - 1;
fBottomControlHeight = kRewindBitmapHeight - 1.0;
fSkipBack = new TransportButton(frame, B_EMPTY_STRING,
kSkipBackBitmapBits,
kPressedSkipBackBitmapBits,
kDisabledSkipBackBitmapBits,
new BMessage(MSG_SKIP_BACKWARDS));
AddChild(fSkipBack);
// Skip Foward
fSkipForward = new TransportButton(frame, B_EMPTY_STRING,
kSkipForwardBitmapBits,
kPressedSkipForwardBitmapBits,
kDisabledSkipForwardBitmapBits,
new BMessage(MSG_SKIP_FORWARD));
AddChild(fSkipForward);
} else {
fSkipBack = NULL;
fSkipForward = NULL;
fSkipBack = new TransportButton(frame, B_EMPTY_STRING,
kSkipBackBitmapBits, kPressedSkipBackBitmapBits,
kDisabledSkipBackBitmapBits, new BMessage(MSG_SKIP_BACKWARDS));
AddChild(fSkipBack);
// Skip Foward
fSkipForward = new TransportButton(frame, B_EMPTY_STRING,
kSkipForwardBitmapBits, kPressedSkipForwardBitmapBits,
kDisabledSkipForwardBitmapBits, new BMessage(MSG_SKIP_FORWARD));
AddChild(fSkipForward);
} else {
fSkipBack = NULL;
fSkipForward = NULL;
}
if (useWindButtons) {
// Forward
fForward = new TransportButton(frame, B_EMPTY_STRING,
kForwardBitmapBits,
kPressedForwardBitmapBits,
kDisabledForwardBitmapBits,
new BMessage(MSG_FORWARD));
kForwardBitmapBits, kPressedForwardBitmapBits,
kDisabledForwardBitmapBits, new BMessage(MSG_FORWARD));
AddChild(fForward);
// Rewind
fRewind = new TransportButton(frame, B_EMPTY_STRING,
kRewindBitmapBits,
kPressedRewindBitmapBits,
kDisabledRewindBitmapBits,
new BMessage(MSG_REWIND));
kRewindBitmapBits, kPressedRewindBitmapBits,
kDisabledRewindBitmapBits, new BMessage(MSG_REWIND));
AddChild(fRewind);
} else {
fForward = NULL;
@ -117,56 +107,56 @@ TransportControlGroup::TransportControlGroup(BRect frame, bool useSkipButtons,
}
// Play Pause
frame.right = kPlayPauseBitmapWidth - 1;
frame.bottom = kPlayPauseBitmapHeight - 1;
frame.right = kPlayPauseBitmapWidth - 1;
frame.bottom = kPlayPauseBitmapHeight - 1;
if (fBottomControlHeight < kPlayPauseBitmapHeight - 1.0)
fBottomControlHeight = kPlayPauseBitmapHeight - 1.0;
fPlayPause = new PlayPauseButton(frame, B_EMPTY_STRING,
kPlayButtonBitmapBits,
kPressedPlayButtonBitmapBits,
kDisabledPlayButtonBitmapBits,
kPlayingPlayButtonBitmapBits,
kPressedPlayingPlayButtonBitmapBits,
kPausedPlayButtonBitmapBits,
kPressedPausedPlayButtonBitmapBits,
new BMessage(MSG_PLAY));
fPlayPause = new PlayPauseButton(frame, B_EMPTY_STRING,
kPlayButtonBitmapBits, kPressedPlayButtonBitmapBits,
kDisabledPlayButtonBitmapBits, kPlayingPlayButtonBitmapBits,
kPressedPlayingPlayButtonBitmapBits, kPausedPlayButtonBitmapBits,
kPressedPausedPlayButtonBitmapBits, new BMessage(MSG_PLAY));
AddChild(fPlayPause);
AddChild(fPlayPause);
// Stop
frame.right = kStopBitmapWidth - 1;
frame.bottom = kStopBitmapHeight - 1;
// Stop
frame.right = kStopBitmapWidth - 1;
frame.bottom = kStopBitmapHeight - 1;
if (fBottomControlHeight < kStopBitmapHeight - 1.0)
fBottomControlHeight = kStopBitmapHeight - 1.0;
fStop = new TransportButton(frame, B_EMPTY_STRING,
kStopButtonBitmapBits,
kPressedStopButtonBitmapBits,
kDisabledStopButtonBitmapBits,
new BMessage(MSG_STOP));
fStop = new TransportButton(frame, B_EMPTY_STRING, kStopButtonBitmapBits,
kPressedStopButtonBitmapBits, kDisabledStopButtonBitmapBits,
new BMessage(MSG_STOP));
AddChild(fStop);
// Mute
frame.right = kSpeakerIconBitmapWidth - 1;
frame.bottom = kSpeakerIconBitmapHeight - 1;
frame.right = kSpeakerIconBitmapWidth - 1;
frame.bottom = kSpeakerIconBitmapHeight - 1;
if (fBottomControlHeight < kSpeakerIconBitmapHeight - 1.0)
fBottomControlHeight = kSpeakerIconBitmapHeight - 1.0;
fMute = new TransportButton(frame, B_EMPTY_STRING,
kSpeakerIconBits,
kPressedSpeakerIconBits,
kSpeakerIconBits,
new BMessage(MSG_SET_MUTE));
fMute = new TransportButton(frame, B_EMPTY_STRING, kSpeakerIconBits,
kPressedSpeakerIconBits, kSpeakerIconBits, new BMessage(MSG_SET_MUTE));
AddChild(fMute);
// Volume Slider
// Volume Slider
fVolumeSlider = new VolumeSlider(BRect(0.0, 0.0, VOLUME_MIN_WIDTH,
kVolumeSliderBitmapHeight - 1.0),
"volume slider",
_DbToGain(_ExponentialToLinear(kVolumeDbMin)) * kVolumeFactor,
_DbToGain(_ExponentialToLinear(kVolumeDbMax)) * kVolumeFactor,
new BMessage(MSG_SET_VOLUME));
fVolumeSlider->SetValue(_DbToGain(_ExponentialToLinear(0.0)) * kVolumeFactor);
kVolumeSliderBitmapHeight - 1.0), "volume slider",
_DbToGain(_ExponentialToLinear(kVolumeDbMin)) * kVolumeFactor,
_DbToGain(_ExponentialToLinear(kVolumeDbMax)) * kVolumeFactor,
new BMessage(MSG_SET_VOLUME));
fVolumeSlider->SetValue(_DbToGain(_ExponentialToLinear(0.0))
* kVolumeFactor);
AddChild(fVolumeSlider);
// Peak view
if (usePeakView) {
fPeakView = new PeakView("peak view", false, false);
AddChild(fPeakView);
fPeakView->GetPreferredSize(&fPeakViewMinWidth, NULL);
} else {
fPeakView = NULL;
}
}
// destructor
@ -469,6 +459,8 @@ TransportControlGroup::_LayoutControls(BRect frame) const
minWidth += fSkipForward->Bounds().Width();
minWidth += fMute->Bounds().Width();
minWidth += VOLUME_MIN_WIDTH;
if (fPeakView)
minWidth += fPeakViewMinWidth;
// layout seek slider
r.bottom = r.top + fSeekSlider->Bounds().Height();
@ -481,7 +473,8 @@ TransportControlGroup::_LayoutControls(BRect frame) const
float currentWidth = frame.Width();
float space = (currentWidth - minWidth) / 6.0;
// apply weighting
space = MIN_SPACE + (space - MIN_SPACE) / VOLUME_SLIDER_LAYOUT_WEIGHT;
space = min_c(MIN_SPACE + (space - MIN_SPACE) / VOLUME_SLIDER_LAYOUT_WEIGHT,
MIN_SPACE * 2.0);
// layout controls with "space" inbetween
r.left = frame.left;
r.top = r.bottom + MIN_SPACE + 1.0;
@ -522,11 +515,24 @@ TransportControlGroup::_LayoutControls(BRect frame) const
r.left = r.right + space + space;
r.right = r.left + fMute->Bounds().Width();
_LayoutControl(fMute, r);
// volume slider
r.left = r.right + SPEAKER_SLIDER_DIST;
// keep speaker icon and volume slider attached
r.right = frame.right;
// layout volume slider
float peakViewWidth = 0.0;
if (fPeakView)
peakViewWidth = (frame.right - r.left) / 2 + space;
r.right = frame.right - peakViewWidth;
_LayoutControl(fVolumeSlider, r, true);
if (fPeakView) {
peakViewWidth -= space;
r.left = r.right + space;
r.right = r.left + peakViewWidth;
_LayoutControl(fPeakView, r, true, true);
}
}
// _MinFrame
@ -547,6 +553,8 @@ TransportControlGroup::_MinFrame() const
minWidth += fSkipForward->Bounds().Width() + MIN_SPACE + MIN_SPACE;
minWidth += fMute->Bounds().Width() + SPEAKER_SLIDER_DIST;
minWidth += VOLUME_MIN_WIDTH;
if (fPeakView)
minWidth += fPeakViewMinWidth;
// add up height of seek slider and heighest control on bottom
float minHeight = 2 * BORDER_INSET;

View File

@ -1,9 +1,6 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
// NOTE: Based on my code in the BeOS interface for the VLC media player
@ -15,6 +12,7 @@
#include <View.h>
class PeakView;
class PlayPauseButton;
class TransportButton;
class SeekSlider;
@ -33,8 +31,9 @@ enum {
class TransportControlGroup : public BView {
public:
TransportControlGroup(BRect frame,
bool useSkipButtons = true,
bool useWindButtons = false);
bool useSkipButtons,
bool usePeakView,
bool useWindButtons);
virtual ~TransportControlGroup();
// BView interface
@ -67,6 +66,9 @@ class TransportControlGroup : public BView {
void SetVolume(float value);
void SetPosition(float value);
PeakView* GetPeakView() const
{ return fPeakView; }
private:
void _LayoutControls(BRect frame) const;
BRect _MinFrame() const;
@ -91,7 +93,7 @@ class TransportControlGroup : public BView {
float _GainToDb(float gain);
SeekSlider* fSeekSlider;
PeakView* fPeakView;
VolumeSlider* fVolumeSlider;
TransportButton* fSkipBack;
@ -103,6 +105,7 @@ class TransportControlGroup : public BView {
TransportButton* fMute;
float fBottomControlHeight;
float fPeakViewMinWidth;
};
#endif // TRANSPORT_CONTROL_GROUP_H

View File

@ -0,0 +1,429 @@
/*
* Copyright (C) 1998-1999 Be Incorporated. All rights reseved.
* Distributed under the terms of the Be Sample Code license.
*
* Copyright (C) 2001-2008 Stephan Aßmus. All rights reserved.
* Distributed under the terms of the MIT license.
*/
#include "PeakView.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <Bitmap.h>
#include <MenuItem.h>
#include <Message.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <PopUpMenu.h>
#include <Window.h>
using std::nothrow;
enum {
MSG_PULSE = 'puls',
MSG_LOCK_PEAKS = 'lpks'
};
// constructor
PeakView::PeakView(const char* name, bool useGlobalPulse, bool displayLabels)
: BView(BRect(0.0, 0.0, 155.0 + 4.0, 10.0 + 4.0),
name, B_FOLLOW_LEFT | B_FOLLOW_TOP,
useGlobalPulse ? B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS
: B_WILL_DRAW | B_FRAME_EVENTS),
fUseGlobalPulse(useGlobalPulse),
fDisplayLabels(displayLabels),
fPeakLocked(false),
fRefreshDelay(20000),
fPulse(NULL),
fCurrentMaxL(0.0),
fLastMaxL(0.0),
fOvershotL(false),
fCurrentMaxR(0.0),
fLastMaxR(0.0),
fOvershotR(false),
fBackBitmap(new BBitmap(BRect(0.0, 0.0, 256.0, 18.0), B_CMAP8)),
fPeakNotificationWhat(0)
{
GetFontHeight(&fFontHeight);
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
SetViewColor(B_TRANSPARENT_COLOR);
FrameResized(Bounds().Width(), Bounds().Height());
if (fDisplayLabels)
ResizeBy(0, ceilf(fFontHeight.ascent + fFontHeight.descent));
}
// destructor
PeakView::~PeakView()
{
delete fPulse;
delete fBackBitmap;
}
// MessageReceived
void
PeakView::MessageReceived(BMessage* message)
{
if (message->what == fPeakNotificationWhat) {
float maxL;
if (message->FindFloat("max", 0, &maxL) < B_OK)
maxL = 0.0;
float maxR;
if (message->FindFloat("max", 1, &maxR) < B_OK)
maxR = 0.0;
SetMax(maxL, maxR);
return;
}
switch (message->what) {
case MSG_PULSE:
Pulse();
break;
case MSG_LOCK_PEAKS:
fPeakLocked = !fPeakLocked;
break;
default:
BView::MessageReceived(message);
break;
}
}
// AttachedToWindow
void
PeakView::AttachedToWindow()
{
if (!fUseGlobalPulse) {
delete fPulse;
BMessage message(MSG_PULSE);
fPulse = new BMessageRunner(BMessenger(this), &message,
fRefreshDelay);
}
}
// DetachedFromWindow
void
PeakView::DetachedFromWindow()
{
delete fPulse;
fPulse = NULL;
}
// MouseDown
void
PeakView::MouseDown(BPoint where)
{
int32 buttons;
if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) < B_OK)
buttons = B_PRIMARY_MOUSE_BUTTON;
if (buttons & B_PRIMARY_MOUSE_BUTTON) {
fOvershotL = false;
fOvershotR = false;
fLastMaxL = fCurrentMaxL;
fLastMaxR = fCurrentMaxR;
} else if (buttons & B_TERTIARY_MOUSE_BUTTON) {
fPeakLocked = !fPeakLocked;
} else {
BPopUpMenu* menu = new BPopUpMenu("peak context menu");
BMenuItem* item = new BMenuItem("Lock Peaks",
new BMessage(MSG_LOCK_PEAKS));
item->SetMarked(fPeakLocked);
menu->AddItem(item);
menu->SetTargetForItems(this);
menu->SetAsyncAutoDestruct(true);
menu->SetFont(be_plain_font);
where = ConvertToScreen(where);
bool keepOpen = false; // ?
if (keepOpen) {
BRect mouseRect(where, where);
mouseRect.InsetBy(-3.0, -3.0);
where += BPoint(3.0, 3.0);
menu->Go(where, true, false, mouseRect, true);
} else {
where += BPoint(3.0, 3.0);
menu->Go(where, true, false, true);
}
}
}
// Draw
void
PeakView::Draw(BRect area)
{
rgb_color background = LowColor();
rgb_color lightShadow = tint_color(background, B_DARKEN_1_TINT);
rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
rgb_color black = tint_color(background, B_DARKEN_MAX_TINT);
BRect r(_BackBitmapFrame());
float width = r.Width();
r.InsetBy(-2.0, -2.0);
// frame
BeginLineArray(9);
AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), lightShadow);
AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top),
lightShadow);
AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom),
light);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), light);
r.InsetBy(1.0, 1.0);
AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), darkShadow);
AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top),
darkShadow);
AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom),
lightShadow);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), lightShadow);
r.InsetBy(1.0, 1.0);
AddLine(BPoint(r.left, (r.top + r.bottom) / 2.0),
BPoint(r.right, (r.top + r.bottom) / 2.0), black);
EndLineArray();
// peak bitmap
if (fBackBitmap)
_DrawBitmap();
// dB
if (fDisplayLabels) {
font_height fh;
GetFontHeight(&fh);
float y = Bounds().bottom;
y -= fh.descent;
DrawString("0", BPoint(4.0 + width - StringWidth("0"), y));
DrawString("-6", BPoint(0.477 * width, y));
DrawString("-12", BPoint(0.227 * width, y));
}
}
// FrameResized
void
PeakView::FrameResized(float width, float height)
{
BRect bitmapFrame = _BackBitmapFrame();
_ResizeBackBitmap(bitmapFrame.IntegerWidth() + 1);
_UpdateBackBitmap();
}
// Pulse
void
PeakView::Pulse()
{
if (!fBackBitmap)
return;
if (!fPeakLocked) {
if (fCurrentMaxL > fLastMaxL)
fLastMaxL = fCurrentMaxL;
if (fCurrentMaxR > fLastMaxR)
fLastMaxR = fCurrentMaxR;
}
_UpdateBackBitmap();
fCurrentMaxL = 0.0;
fCurrentMaxR = 0.0;
_DrawBitmap();
Flush();
}
// GetPreferredSize
void
PeakView::GetPreferredSize(float* _width, float* _height)
{
float minWidth = 0;
float minHeight = 0;
if (fBackBitmap) {
minWidth = 20 + 4;
minHeight = 3 + 4;
}
if (fDisplayLabels) {
font_height fh;
GetFontHeight(&fh);
minWidth = max_c(60.0, minWidth);
minHeight += ceilf(fh.ascent + fh.descent);
}
if (_width)
*_width = minWidth;
if (_height)
*_height = minHeight;
}
// IsValid
bool
PeakView::IsValid() const
{
return fBackBitmap && fBackBitmap->IsValid();
}
// SetPeakRefreshDelay
void
PeakView::SetPeakRefreshDelay(bigtime_t delay)
{
if (fRefreshDelay == delay)
return;
fRefreshDelay = delay;
if (fPulse)
fPulse->SetInterval(fRefreshDelay);
}
// SetPeakNotificationWhat
void
PeakView::SetPeakNotificationWhat(uint32 what)
{
fPeakNotificationWhat = what;
}
// SetMax
void
PeakView::SetMax(float maxL, float maxR)
{
if (fCurrentMaxL < maxL)
fCurrentMaxL = maxL;
if (fCurrentMaxR < maxR)
fCurrentMaxR = maxR;
if (fCurrentMaxL > 1.0)
fOvershotL = true;
if (fCurrentMaxR > 1.0)
fOvershotR = true;
}
// #pragma mark -
// _BackBitmapFrame
BRect
PeakView::_BackBitmapFrame() const
{
BRect frame = Bounds();
frame.InsetBy(2, 2);
if (fDisplayLabels)
frame.bottom -= ceilf(fFontHeight.ascent + fFontHeight.descent);
return frame;
}
// _ResizeBackBitmap
void
PeakView::_ResizeBackBitmap(int32 width)
{
if (fBackBitmap) {
if (fBackBitmap->Bounds().IntegerWidth() + 1 == width)
return;
}
delete fBackBitmap;
fBackBitmap = new (nothrow) BBitmap(BRect(0, 0, width - 1, 1), 0, B_RGB32);
if (!fBackBitmap || !fBackBitmap->IsValid()) {
delete fBackBitmap;
fBackBitmap = NULL;
return;
}
memset(fBackBitmap->Bits(), 0, fBackBitmap->BitsLength());
}
// _UpdateBackBitmap
void
PeakView::_UpdateBackBitmap()
{
if (!fBackBitmap)
return;
uint8* l = (uint8*)fBackBitmap->Bits();
uint8* r = l + fBackBitmap->BytesPerRow();
uint32 width = fBackBitmap->Bounds().IntegerWidth() + 1;
_RenderSpan(l, width, fCurrentMaxL, fLastMaxL, fOvershotL);
_RenderSpan(r, width, fCurrentMaxR, fLastMaxR ,fOvershotR);
}
// _RenderSpan
void
PeakView:: _RenderSpan(uint8* span, uint32 width, float current, float peak,
bool overshot)
{
uint8 emptyR = 15;
uint8 emptyG = 36;
uint8 emptyB = 16;
uint8 fillR = 41;
uint8 fillG = 120;
uint8 fillB = 45;
uint8 currentR = 45;
uint8 currentG = 255;
uint8 currentB = 45;
uint8 lastR = 255;
uint8 lastG = 229;
uint8 lastB = 87;
uint8 overR = 255;
uint8 overG = 89;
uint8 overB = 7;
uint8 kFadeFactor = 180;
uint32 split = (uint32)(current * (width - 2) + 0.5);
split += split & 1;
uint32 last = (uint32)(peak * (width - 2) + 0.5);
last += last & 1;
uint32 over = overshot ? width - 1 : width;
over += over & 1;
for (uint32 x = 0; x < width - 1; x += 2) {
uint8 fadedB = (uint8)(((int)span[0] * kFadeFactor) >> 8);
uint8 fadedG = (uint8)(((int)span[1] * kFadeFactor) >> 8);
uint8 fadedR = (uint8)(((int)span[2] * kFadeFactor) >> 8);
if (x < split) {
span[0] = max_c(fillB, fadedB);
span[1] = max_c(fillG, fadedG);
span[2] = max_c(fillR, fadedR);
} else if (x == split) {
span[0] = currentB;
span[1] = currentG;
span[2] = currentR;
} else if (x > split) {
span[0] = max_c(emptyB, fadedB);
span[1] = max_c(emptyG, fadedG);
span[2] = max_c(emptyR, fadedR);
}
if (x == last) {
span[0] = lastB;
span[1] = lastG;
span[2] = lastR;
}
if (x == over) {
span[0] = overB;
span[1] = overG;
span[2] = overR;
}
span += 8;
}
}
// _DrawBitmap
void
PeakView::_DrawBitmap()
{
BRect r = _BackBitmapFrame();
BRect topHalf = r;
topHalf.bottom = (r.top + r.bottom) / 2.0 - 1;
BRect bottomHalf = r;
bottomHalf.top = topHalf.bottom + 2;
BRect bitmapRect = fBackBitmap->Bounds();
bitmapRect.bottom = bitmapRect.top;
DrawBitmapAsync(fBackBitmap, bitmapRect, topHalf);
bitmapRect.OffsetBy(0, 1);
DrawBitmapAsync(fBackBitmap, bitmapRect, bottomHalf);
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 1998-1999 Be Incorporated. All rights reseved.
* Distributed under the terms of the Be Sample Code license.
*
* Copyright (C) 2001-2008 Stephan Aßmus. All rights reserved.
* Distributed under the terms of the MIT license.
*/
/*----------------------------------------------------------
PURPOSE:
gui class for displaying stereo audio peak level info
FEATURES:
- uses a bitmap, but not a view accepting one, to redraw
without flickering.
- can be configured to use it's own message runner instead
of the windows current pulse (in case you have text views in
the window as well, which use a slow pulse for cursor blinking)
- if used with own message runner, refresh delay is configurable
- can be used in a dynamic liblayout gui
USAGE:
To display the peak level of your streaming audio, just
calculate the local maximum of both channels within your
audio buffer and call SetMax() once for every buffer. The
PeakView will take care of the rest.
min = 0.0
max = 1.0
----------------------------------------------------------*/
#ifndef PEAK_VIEW_H
#define PEAL_VIEW_H
#include <View.h>
class BBitmap;
class BMessageRunner;
class PeakView : public BView {
public:
PeakView(const char* name,
bool useGlobalPulse = true,
bool displayLabels = true);
virtual ~PeakView();
// BView interface
virtual void MessageReceived(BMessage* message);
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void MouseDown(BPoint where);
virtual void Draw(BRect area);
virtual void FrameResized(float width, float height);
virtual void Pulse();
virtual void GetPreferredSize(float* _width,
float* _height);
// PeakView
bool IsValid() const;
void SetPeakRefreshDelay(bigtime_t delay);
void SetPeakNotificationWhat(uint32 what);
void SetMax(float maxL, float maxR);
private:
BRect _BackBitmapFrame() const;
void _ResizeBackBitmap(int32 width);
void _UpdateBackBitmap();
void _RenderSpan(uint8* span, uint32 width,
float current, float peak, bool overshot);
void _DrawBitmap();
bool fUseGlobalPulse;
bool fDisplayLabels;
bool fPeakLocked;
bigtime_t fRefreshDelay;
BMessageRunner* fPulse;
float fCurrentMaxL;
float fLastMaxL;
bool fOvershotL;
float fCurrentMaxR;
float fLastMaxR;
bool fOvershotR;
BBitmap* fBackBitmap;
font_height fFontHeight;
uint32 fPeakNotificationWhat;
};
#endif // PEAK_VIEW_H

View File

@ -1,13 +1,10 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
// NOTE: Based on my code in the BeOS interface for the VLC media player
// that I did during the VLC 0.4.3 - 0.4.6 times. Code not done by me
// that I did during the VLC 0.4.3 - 0.4.6 times. Code not written by me
// removed. -Stephan Aßmus
#include "SeekSlider.h"
@ -32,7 +29,7 @@ const char* kDisabledSeekMessage = "Drop files to play";
SeekSlider::SeekSlider(BRect frame, const char* name, BMessage* message,
int32 minValue, int32 maxValue)
: BControl(frame, name, NULL, message, B_FOLLOW_LEFT | B_FOLLOW_TOP,
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS)
, fTracking(false)
, fLastTrackTime(0)
, fKnobPos(_KnobPosFor(Bounds(), Value()))
@ -101,9 +98,9 @@ SeekSlider::Draw(BRect updateRect)
rgb_color dotGrey = midShadow;
rgb_color dotGreen = greenShadow;
// draw frame
_StrokeFrame(r, softShadow, softShadow, softLight, softLight);
_StrokeFrame(r, softShadow, softShadow, light, light);
r.InsetBy(1.0, 1.0);
_StrokeFrame(r, black, black, light, light);
_StrokeFrame(r, black, black, softShadow, softShadow);
if (IsEnabled()) {
// *** enabled look ***
r.InsetBy(1.0, 1.0);
@ -280,6 +277,13 @@ SeekSlider::ResizeToPreferred()
}
void
SeekSlider::FrameResized(float width, float height)
{
_SetKnobPosition(_KnobPosFor(Bounds(), Value()));
}
void
SeekSlider::SetPosition(float position)
{

View File

@ -1,9 +1,6 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef SEEK_SLIDER_H
@ -15,10 +12,8 @@
class SeekSlider : public BControl {
public:
SeekSlider(BRect frame,
const char* name,
BMessage* message,
int32 minValue,
int32 maxValue);
const char* name, BMessage* message,
int32 minValue, int32 maxValue);
virtual ~SeekSlider();
@ -28,9 +23,10 @@ class SeekSlider : public BControl {
virtual void Draw(BRect updateRect);
virtual void MouseDown(BPoint where);
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage);
const BMessage* dragMessage);
virtual void MouseUp(BPoint where);
virtual void ResizeToPreferred();
virtual void FrameResized(float width, float height);
// SeekSlider
void SetPosition(float position);
@ -41,10 +37,8 @@ private:
int32 _KnobPosFor(BRect bounds,
int32 value) const;
void _StrokeFrame(BRect frame,
rgb_color left,
rgb_color top,
rgb_color right,
rgb_color bottom);
rgb_color left, rgb_color top,
rgb_color right, rgb_color bottom);
void _SetKnobPosition(int32 knobPos);

View File

@ -51,7 +51,8 @@ NodeManager::NodeManager()
fVideoTarget(NULL),
fAudioSupplier(NULL),
fVideoSupplier(NULL),
fVideoBounds(0, 0, -1, -1)
fVideoBounds(0, 0, -1, -1),
fPeakListener(NULL)
{
}
@ -233,13 +234,22 @@ NodeManager::SetVolume(float percent)
// our audio node...
}
// SetPeakListener
void
NodeManager::SetPeakListener(BHandler* handler)
{
fPeakListener = handler;
if (fAudioProducer)
fAudioProducer->SetPeakListener(fPeakListener);
}
// #pragma mark -
// _SetUpNodes
status_t
NodeManager::_SetUpNodes(color_space preferredVideoFormat)
{
printf("NodeManager::_SetUpNodes()\n");
TRACE("NodeManager::_SetUpNodes()\n");
// find the media roster
fStatus = B_OK;
@ -425,6 +435,7 @@ status_t
NodeManager::_SetUpAudioNodes()
{
fAudioProducer = new AudioProducer("MediaPlayer Audio Out", fAudioSupplier);
fAudioProducer->SetPeakListener(fPeakListener);
fStatus = fMediaRoster->RegisterNode(fAudioProducer);
if (fStatus != B_OK) {
print_error("unable to register audio producer node!\n", fStatus);

View File

@ -62,6 +62,8 @@ class NodeManager : public PlaybackManager {
virtual void SetVolume(float percent);
void SetPeakListener(BHandler* handler);
private:
status_t _SetUpNodes(color_space preferredVideoFormat);
status_t _SetUpVideoNodes(
@ -103,6 +105,8 @@ private:
AudioSupplier* fAudioSupplier;
VideoSupplier* fVideoSupplier;
BRect fVideoBounds;
BHandler* fPeakListener;
};

View File

@ -18,6 +18,8 @@
#include <TimeSource.h>
#include "AudioSupplier.h"
#include "EventQueue.h"
#include "MessageEvent.h"
#define DEBUG_TO_FILE 0
@ -94,7 +96,9 @@ AudioProducer::AudioProducer(const char* name, AudioSupplier* supplier,
fFramesSent(0),
fStartTime(0),
fSupplier(supplier),
fRunning(false)
fRunning(false),
fPeakListener(NULL)
{
TRACE("%p->AudioProducer::AudioProducer(%s, %p, %d)\n", this, name,
supplier, lowLatency);
@ -661,6 +665,13 @@ AudioProducer::SetRunning(bool running)
}
void
AudioProducer::SetPeakListener(BHandler* handler)
{
fPeakListener = handler;
}
// #pragma mark -
@ -739,6 +750,42 @@ AudioProducer::_FillNextBuffer(bigtime_t eventTime)
}
#endif // DEBUG_TO_FILE
if (fPeakListener
&& fOutput.format.u.raw_audio.format
== media_raw_audio_format::B_AUDIO_FLOAT) {
// TODO: extend the peak notifier for other sample formats
int32 channels = fOutput.format.u.raw_audio.channel_count;
float max[channels];
float min[channels];
for (int32 i = 0; i < channels; i++) {
max[i] = -1.0;
min[i] = 1.0;
}
float* sample = (float*)buffer->Data();
for (uint32 i = 0; i < frameCount; i++) {
for (int32 k = 0; k < channels; k++) {
if (*sample < min[k])
min[k] = *sample;
if (*sample > max[k])
max[k] = *sample;
sample++;
}
}
BMessage message(MSG_PEAK_NOTIFICATION);
for (int32 i = 0; i < channels; i++) {
float maxAbs = max_c(fabs(min[i]), fabs(max[i]));
message.AddFloat("max", maxAbs);
}
bigtime_t realTime = TimeSource()->RealTimeFor(performanceTime, 0);
realTime -= 2000;
// deliver event about one video frame earlier to account
// for latency between app_server and client
MessageEvent* event = new (nothrow) MessageEvent(realTime,
fPeakListener, message);
if (event != NULL)
EventQueue::Default().AddEvent(event);
}
return buffer;
}

View File

@ -13,6 +13,11 @@
#include <MediaEventLooper.h>
class AudioSupplier;
class BHandler;
enum {
MSG_PEAK_NOTIFICATION = 'pknt'
};
class AudioProducer : public BBufferProducer, public BMediaEventLooper {
public:
@ -95,6 +100,9 @@ public:
void SetRunning(bool running);
// AudioProducer
void SetPeakListener(BHandler* handler);
private:
status_t _AllocateBuffers(media_format* format);
BBuffer* _FillNextBuffer(bigtime_t eventTime);
@ -116,6 +124,8 @@ private:
AudioSupplier* fSupplier;
volatile bool fRunning;
BHandler* fPeakListener;
};
#endif // AUDIO_PRODUCER_H

View File

@ -11,7 +11,16 @@
MessageEvent::MessageEvent(bigtime_t time, BHandler* handler, uint32 command)
: Event(time),
AbstractLOAdapter(handler),
fCommand(command)
fMessage(command)
{
}
MessageEvent::MessageEvent(bigtime_t time, BHandler* handler,
const BMessage& message)
: Event(time),
AbstractLOAdapter(handler),
fMessage(message)
{
}
@ -31,7 +40,7 @@ MessageEvent::~MessageEvent()
void
MessageEvent::Execute()
{
BMessage msg(fCommand);
BMessage msg(fMessage);
msg.AddInt64("time", Time());
DeliverMessage(msg);
}

View File

@ -7,12 +7,14 @@
#define MESSAGE_EVENT_H
#include <Message.h>
#include "AbstractLOAdapter.h"
#include "Event.h"
enum {
MSG_EVENT = 'evnt',
MSG_EVENT = 'evnt'
};
@ -21,6 +23,9 @@ class MessageEvent : public Event, public AbstractLOAdapter {
MessageEvent(bigtime_t time,
BHandler* handler,
uint32 command = MSG_EVENT);
MessageEvent(bigtime_t time,
BHandler* handler,
const BMessage& message);
MessageEvent(bigtime_t time,
const BMessenger& messenger);
virtual ~MessageEvent();
@ -28,7 +33,7 @@ class MessageEvent : public Event, public AbstractLOAdapter {
virtual void Execute();
private:
uint32 fCommand;
BMessage fMessage;
};
#endif // MESSAGE_EVENT_H