Added SoundRecorder (inspired by SoundCapture from beos samples)
Alpha state git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13035 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -14,6 +14,7 @@ SubInclude OBOS_TOP src apps people ;
|
||||
SubInclude OBOS_TOP src apps poorman ;
|
||||
SubInclude OBOS_TOP src apps pulse ;
|
||||
SubInclude OBOS_TOP src apps showimage ;
|
||||
SubInclude OBOS_TOP src apps soundrecorder ;
|
||||
SubInclude OBOS_TOP src apps stylededit ;
|
||||
SubInclude OBOS_TOP src apps terminal ;
|
||||
SubInclude OBOS_TOP src apps tracker ;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 <Bitmap.h>
|
||||
#include <Debug.h>
|
||||
#include <Screen.h>
|
||||
|
||||
#include "DrawingTidbits.h"
|
||||
|
||||
rgb_color
|
||||
ShiftColor(rgb_color color, float percent)
|
||||
{
|
||||
rgb_color result = {
|
||||
ShiftComponent(color.red, percent),
|
||||
ShiftComponent(color.green, percent),
|
||||
ShiftComponent(color.blue, percent),
|
||||
0
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool
|
||||
CompareColors(const rgb_color a, const rgb_color b)
|
||||
{
|
||||
return a.red == b.red
|
||||
&& a.green == b.green
|
||||
&& a.blue == b.blue
|
||||
&& a.alpha == b.alpha;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const rgb_color &a, const rgb_color &b)
|
||||
{
|
||||
return CompareColors(a, b);
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const rgb_color &a, const rgb_color &b)
|
||||
{
|
||||
return !CompareColors(a, b);
|
||||
}
|
||||
|
||||
void
|
||||
ReplaceColor(BBitmap *bitmap, rgb_color from, rgb_color to)
|
||||
{
|
||||
ASSERT(bitmap->ColorSpace() == B_COLOR_8_BIT); // other color spaces not implemented yet
|
||||
|
||||
BScreen screen(B_MAIN_SCREEN_ID);
|
||||
uint32 fromIndex = screen.IndexForColor(from);
|
||||
uint32 toIndex = screen.IndexForColor(to);
|
||||
|
||||
uchar *bits = (uchar *)bitmap->Bits();
|
||||
int32 bitsLength = bitmap->BitsLength();
|
||||
for (int32 index = 0; index < bitsLength; index++)
|
||||
if (bits[index] == fromIndex)
|
||||
bits[index] = toIndex;
|
||||
}
|
||||
|
||||
void
|
||||
ReplaceTransparentColor(BBitmap *bitmap, rgb_color with)
|
||||
{
|
||||
ASSERT(bitmap->ColorSpace() == B_COLOR_8_BIT); // other color spaces not implemented yet
|
||||
|
||||
BScreen screen(B_MAIN_SCREEN_ID);
|
||||
uint32 withIndex = screen.IndexForColor(with);
|
||||
|
||||
uchar *bits = (uchar *)bitmap->Bits();
|
||||
int32 bitsLength = bitmap->BitsLength();
|
||||
for (int32 index = 0; index < bitsLength; index++)
|
||||
if (bits[index] == B_TRANSPARENT_8_BIT)
|
||||
bits[index] = withIndex;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#ifndef __DRAWING_TIBITS__
|
||||
#define __DRAWING_TIBITS__
|
||||
|
||||
#include <GraphicsDefs.h>
|
||||
|
||||
rgb_color ShiftColor(rgb_color , float );
|
||||
|
||||
bool operator==(const rgb_color &, const rgb_color &);
|
||||
bool operator!=(const rgb_color &, const rgb_color &);
|
||||
|
||||
inline uchar
|
||||
ShiftComponent(uchar component, float percent)
|
||||
{
|
||||
// change the color by <percent>, make sure we aren't rounding
|
||||
// off significant bits
|
||||
if (percent >= 1)
|
||||
return (uchar)(component * (2 - percent));
|
||||
else
|
||||
return (uchar)(255 - percent * (255 - component));
|
||||
}
|
||||
|
||||
inline rgb_color
|
||||
Color(int32 r, int32 g, int32 b, int32 alpha = 255)
|
||||
{
|
||||
rgb_color result;
|
||||
result.red = r;
|
||||
result.green = g;
|
||||
result.blue = b;
|
||||
result.alpha = alpha;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const rgb_color kWhite = { 255, 255, 255, 255};
|
||||
const rgb_color kBlack = { 0, 0, 0, 255};
|
||||
|
||||
const float kDarkness = 1.06;
|
||||
const float kDimLevel = 0.6;
|
||||
|
||||
void ReplaceColor(BBitmap *bitmap, rgb_color from, rgb_color to);
|
||||
void ReplaceTransparentColor(BBitmap *bitmap, rgb_color with);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
/*******************************************************************************
|
||||
/
|
||||
/ File: FileUtils.cpp
|
||||
/
|
||||
/ Description: Utility functions for copying file data and attributes.
|
||||
/
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fs_attr.h>
|
||||
#include "array_delete.h"
|
||||
#include "FileUtils.h"
|
||||
|
||||
status_t CopyFileData(BFile& dst, BFile& src)
|
||||
{
|
||||
struct stat src_stat;
|
||||
status_t err = src.GetStat(&src_stat);
|
||||
if (err != B_OK) {
|
||||
printf("couldn't get stat: %#010lx\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
size_t bufSize = src_stat.st_blksize;
|
||||
if (! bufSize) {
|
||||
bufSize = 32768;
|
||||
}
|
||||
|
||||
char* buf = new char[bufSize];
|
||||
array_delete<char> bufDelete(buf);
|
||||
|
||||
printf("copy data, bufSize = %ld\n", bufSize);
|
||||
// copy data
|
||||
while (true) {
|
||||
ssize_t bytes = src.Read(buf, bufSize);
|
||||
if (bytes > 0) {
|
||||
ssize_t result = dst.Write(buf, bytes);
|
||||
if (result != bytes) {
|
||||
printf("result = %#010lx, bytes = %#010lx\n", (uint32) result,
|
||||
(uint32) bytes);
|
||||
return B_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (bytes < 0) {
|
||||
printf(" bytes = %#010lx\n", (uint32) bytes);
|
||||
return bytes;
|
||||
} else {
|
||||
// EOF
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finish up miscellaneous stat stuff
|
||||
dst.SetPermissions(src_stat.st_mode);
|
||||
dst.SetOwner(src_stat.st_uid);
|
||||
dst.SetGroup(src_stat.st_gid);
|
||||
dst.SetModificationTime(src_stat.st_mtime);
|
||||
dst.SetCreationTime(src_stat.st_crtime);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t CopyAttributes(BNode& dst, BNode& src)
|
||||
{
|
||||
// copy attributes
|
||||
src.RewindAttrs();
|
||||
char name[B_ATTR_NAME_LENGTH];
|
||||
while (src.GetNextAttrName(name) == B_OK) {
|
||||
attr_info info;
|
||||
if (src.GetAttrInfo(name, &info) == B_OK) {
|
||||
size_t bufSize = info.size;
|
||||
char* buf = new char[bufSize];
|
||||
array_delete<char> bufDelete = buf;
|
||||
|
||||
// copy one attribute
|
||||
ssize_t bytes = src.ReadAttr(name, info.type, 0, buf, bufSize);
|
||||
if (bytes > 0) {
|
||||
dst.WriteAttr(name, info.type, 0, buf, bufSize);
|
||||
} else {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t CopyFile(BFile& dst, BFile& src)
|
||||
{
|
||||
status_t err = CopyFileData(dst, src);
|
||||
if (err != B_OK)
|
||||
return err;
|
||||
|
||||
return CopyAttributes(dst, src);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*******************************************************************************
|
||||
/
|
||||
/ File: FileUtils.h
|
||||
/
|
||||
/ Description: Utility functions for copying file data and attributes.
|
||||
/
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#if ! defined( _FileUtils_h )
|
||||
#define _FileUtils_h
|
||||
|
||||
#include <File.h>
|
||||
|
||||
status_t CopyFile(BFile& dest, BFile& src);
|
||||
status_t CopyFileData(BFile& dst, BFile& src);
|
||||
status_t CopyAttributes(BNode& dst, BNode& src);
|
||||
|
||||
#endif /* _FileUtils_h */
|
||||
@@ -0,0 +1,19 @@
|
||||
SubDir OBOS_TOP src apps soundrecorder ;
|
||||
|
||||
App SoundRecorder :
|
||||
DrawingTidbits.cpp
|
||||
FileUtils.cpp
|
||||
RecorderApp.cpp
|
||||
RecorderWindow.cpp
|
||||
ScopeView.cpp
|
||||
SoundConsumer.cpp
|
||||
SoundListView.cpp
|
||||
SoundUtils.cpp
|
||||
TrackSlider.cpp
|
||||
TransportButton.cpp
|
||||
UpDownButton.cpp
|
||||
VUView.cpp
|
||||
VolumeSlider.cpp
|
||||
: libbe.so libmedia.so libtracker.so
|
||||
: SoundRecorder.rdef
|
||||
;
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 "RecorderApp.h"
|
||||
#include "RecorderWindow.h"
|
||||
|
||||
|
||||
RecorderApp::RecorderApp(const char * signature) :
|
||||
BApplication(signature), fRecorderWin(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
RecorderApp::~RecorderApp()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RecorderApp::ReadyToRun()
|
||||
{
|
||||
BApplication::ReadyToRun();
|
||||
fRecorderWin = new RecorderWindow();
|
||||
fRecorderWin->Show();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
RecorderApp app("application/x-vnd.Haiku-SoundRecorder");
|
||||
app.Run();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#ifndef RECORDERAPP_H
|
||||
#define RECORDERAPP_H
|
||||
|
||||
#include <Application.h>
|
||||
|
||||
class RecorderWindow;
|
||||
|
||||
class RecorderApp : public BApplication {
|
||||
public:
|
||||
RecorderApp(const char * signature);
|
||||
virtual ~RecorderApp();
|
||||
|
||||
virtual void ReadyToRun();
|
||||
|
||||
private:
|
||||
RecorderWindow* fRecorderWin;
|
||||
};
|
||||
|
||||
#endif /* RECORDERAPP_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#ifndef RECORDERWINDOW_H
|
||||
#define RECORDERWINDOW_H
|
||||
|
||||
#include <Directory.h>
|
||||
#include <Entry.h>
|
||||
#include <File.h>
|
||||
#include <FilePanel.h>
|
||||
#include <MediaFile.h>
|
||||
#include <MediaNode.h>
|
||||
#include <MediaTrack.h>
|
||||
#include <SoundPlayer.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "ScopeView.h"
|
||||
#include "TransportButton.h"
|
||||
#include "TrackSlider.h"
|
||||
#include "UpDownButton.h"
|
||||
#include "VolumeSlider.h"
|
||||
#include "VUView.h"
|
||||
|
||||
class BMediaRoster;
|
||||
class BBox;
|
||||
class BButton;
|
||||
class BCheckBox;
|
||||
class BTextControl;
|
||||
class BMenuField;
|
||||
class SoundConsumer;
|
||||
class SoundListView;
|
||||
class BScrollView;
|
||||
class BSlider;
|
||||
class BStringView;
|
||||
|
||||
class RecorderWindow : public BWindow {
|
||||
public:
|
||||
RecorderWindow();
|
||||
virtual ~RecorderWindow();
|
||||
|
||||
|
||||
virtual bool QuitRequested();
|
||||
virtual void MessageReceived(BMessage * message);
|
||||
|
||||
enum {
|
||||
RECORD = 'cw00', // command messages
|
||||
PLAY,
|
||||
STOP,
|
||||
REWIND,
|
||||
FORWARD,
|
||||
SAVE,
|
||||
VIEW_LIST,
|
||||
INPUT_SELECTED = 'cW00', // control messages
|
||||
LENGTH_CHANGED,
|
||||
SOUND_SELECTED,
|
||||
STOP_PLAYING,
|
||||
STOP_RECORDING,
|
||||
RECORD_PERIOD,
|
||||
PLAY_PERIOD,
|
||||
UPDATE_TRACKSLIDER,
|
||||
POSITION_CHANGED
|
||||
};
|
||||
|
||||
void AddSoundItem(const BEntry& entry, bool temp = false);
|
||||
|
||||
private:
|
||||
BMediaRoster * fRoster;
|
||||
VUView *fVUView;
|
||||
ScopeView *fScopeView;
|
||||
RecordButton * fRecordButton;
|
||||
PlayPauseButton * fPlayButton;
|
||||
TransportButton * fStopButton;
|
||||
TransportButton * fRewindButton;
|
||||
TransportButton * fForwardButton;
|
||||
TransportButton * fSaveButton;
|
||||
VolumeSlider *fVolumeSlider;
|
||||
TrackSlider *fTrackSlider;
|
||||
UpDownButton * fUpDownButton;
|
||||
BTextControl * fLengthControl;
|
||||
BMenuField * fInputField;
|
||||
SoundConsumer * fRecordNode;
|
||||
BSoundPlayer * fPlayer;
|
||||
bool fRecording;
|
||||
SoundListView * fSoundList;
|
||||
BDirectory fTempDir;
|
||||
int fTempCount;
|
||||
|
||||
BBox * fBottomBox;
|
||||
BBox * fFileInfoBox;
|
||||
BStringView *fFilename;
|
||||
BStringView *fFormat;
|
||||
BStringView *fCompression;
|
||||
BStringView *fChannels;
|
||||
BStringView *fSampleSize;
|
||||
BStringView *fSampleRate;
|
||||
BStringView *fDuration;
|
||||
|
||||
enum BtnState {
|
||||
btnPaused,
|
||||
btnRecording,
|
||||
btnPlaying
|
||||
};
|
||||
BtnState fButtonState;
|
||||
BEntry fRecEntry;
|
||||
|
||||
BFile fRecFile;
|
||||
off_t fRecLimit;
|
||||
off_t fRecSize;
|
||||
|
||||
media_node fAudioInputNode;
|
||||
media_output fAudioOutput;
|
||||
media_input fRecInput;
|
||||
|
||||
BMediaFile *fPlayFile;
|
||||
media_format fPlayFormat;
|
||||
BMediaTrack *fPlayTrack;
|
||||
int64 fPlayLimit;
|
||||
int64 fPlayFrame;
|
||||
|
||||
media_node fAudioMixerNode;
|
||||
|
||||
BFilePanel fSavePanel;
|
||||
|
||||
status_t InitWindow();
|
||||
|
||||
void Record(BMessage * message);
|
||||
void Play(BMessage * message);
|
||||
void Stop(BMessage * message);
|
||||
void Save(BMessage * message);
|
||||
void DoSave(BMessage * message);
|
||||
void Input(BMessage * message);
|
||||
void Length(BMessage * message);
|
||||
void Selected(BMessage * message);
|
||||
|
||||
status_t MakeRecordConnection(const media_node & input);
|
||||
status_t BreakRecordConnection();
|
||||
status_t StopRecording();
|
||||
|
||||
status_t MakePlayConnection(const media_multi_audio_format & format);
|
||||
status_t BreakPlayConnection();
|
||||
status_t StopPlaying();
|
||||
|
||||
status_t NewTempName(char * buffer);
|
||||
void CalcSizes(float min_width, float min_height);
|
||||
void SetButtonState(BtnState state);
|
||||
void UpdateButtons();
|
||||
void UpdatePlayFile();
|
||||
void ErrorAlert(const char * action, status_t err);
|
||||
|
||||
static void RecordFile(void * cookie, bigtime_t timestamp, void * data, size_t size, const media_raw_audio_format & format);
|
||||
static void NotifyRecordFile(void * cookie, int32 code, ...);
|
||||
|
||||
static void PlayFile(void * cookie, void * data, size_t size, const media_raw_audio_format & format);
|
||||
static void NotifyPlayFile(void * cookie, BSoundPlayer::sound_player_notification code, ...);
|
||||
|
||||
void RefsReceived(BMessage *msg);
|
||||
};
|
||||
|
||||
#endif /* RECORDERWINDOW_H */
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* 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 <Screen.h>
|
||||
#include <Window.h>
|
||||
#include "DrawingTidbits.h"
|
||||
#include "ScopeView.h"
|
||||
|
||||
|
||||
ScopeView::ScopeView(BRect rect, uint32 resizeFlags)
|
||||
: BView(rect, "vumeter", resizeFlags, B_WILL_DRAW | B_FRAME_EVENTS),
|
||||
fThreadId(-1),
|
||||
fBitmap(NULL),
|
||||
fIsRendering(false),
|
||||
fMediaTrack(NULL),
|
||||
fQuitting(false),
|
||||
fMainTime(0),
|
||||
fRightTime(1000000),
|
||||
fLeftTime(0),
|
||||
fTotalTime(1000000)
|
||||
{
|
||||
fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true);
|
||||
memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
|
||||
|
||||
rect.OffsetToSelf(B_ORIGIN);
|
||||
rect.right -= 2;
|
||||
fBitmapView = new BView(rect.OffsetToSelf(B_ORIGIN), "bitmapView", B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW);
|
||||
fBitmap->AddChild(fBitmapView);
|
||||
|
||||
fRenderSem = create_sem(0, "scope rendering");
|
||||
fHeight = Bounds().Height();
|
||||
}
|
||||
|
||||
|
||||
ScopeView::~ScopeView()
|
||||
{
|
||||
delete_sem(fRenderSem);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopeView::AttachedToWindow()
|
||||
{
|
||||
SetViewColor(B_TRANSPARENT_COLOR);
|
||||
Run();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopeView::DetachedFromWindow()
|
||||
{
|
||||
Quit();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopeView::Draw(BRect updateRect)
|
||||
{
|
||||
BRect bounds = Bounds();
|
||||
if (!fIsRendering)
|
||||
DrawBitmapAsync(fBitmap, BPoint(2,0));
|
||||
|
||||
float x = 2;
|
||||
if (fTotalTime !=0)
|
||||
x = 2 + (fMainTime - fLeftTime) * (bounds.right - 2) / (fRightTime - fLeftTime);
|
||||
SetHighColor(60,255,40);
|
||||
StrokeLine(BPoint(x, bounds.top), BPoint(x, bounds.bottom));
|
||||
|
||||
Sync();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopeView::Run()
|
||||
{
|
||||
fThreadId = spawn_thread(&RenderLaunch, "Scope view", B_NORMAL_PRIORITY, this);
|
||||
if (fThreadId < 0)
|
||||
return;
|
||||
resume_thread(fThreadId);
|
||||
}
|
||||
|
||||
void
|
||||
ScopeView::Quit()
|
||||
{
|
||||
delete_sem(fRenderSem);
|
||||
fQuitting = true;
|
||||
snooze(10000);
|
||||
kill_thread(fThreadId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32
|
||||
ScopeView::RenderLaunch(void *data)
|
||||
{
|
||||
ScopeView *scope = (ScopeView*) data;
|
||||
scope->RenderLoop();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopeView::RenderLoop()
|
||||
{
|
||||
|
||||
while (!fQuitting) {
|
||||
|
||||
if (acquire_sem(fRenderSem)!=B_OK)
|
||||
continue;
|
||||
|
||||
fIsRendering = true;
|
||||
|
||||
int32 frame_size = (fPlayFormat.u.raw_audio.format & 0xf) * fPlayFormat.u.raw_audio.channel_count;
|
||||
int64 totalFrames = fMediaTrack->CountFrames();
|
||||
int16 samples[fPlayFormat.u.raw_audio.buffer_size / (fPlayFormat.u.raw_audio.format & 0xf)];
|
||||
int64 frames = 0;
|
||||
int64 sum = 0;
|
||||
int64 framesIndex = 0;
|
||||
int32 sumCount = 0;
|
||||
fMediaTrack->SeekToFrame(&frames);
|
||||
|
||||
printf("begin computing\n");
|
||||
|
||||
int32 previewIndex = 0;
|
||||
|
||||
while (fMediaTrack->ReadFrames(samples, &frames) == B_OK) {
|
||||
//printf("reading block\n");
|
||||
framesIndex = 0;
|
||||
|
||||
while (framesIndex < frames) {
|
||||
|
||||
for (; framesIndex < frames && sumCount < totalFrames/20000; framesIndex++, sumCount++) {
|
||||
sum += samples[2*framesIndex];
|
||||
sum += samples[2*framesIndex+1];
|
||||
}
|
||||
|
||||
if (previewIndex >= 20000) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sumCount >= totalFrames/20000) {
|
||||
//printf("computing block %ld, sumCount %ld\n", previewIndex, sumCount);
|
||||
fPreview[previewIndex++] = (int32)(sum / 2 /(totalFrames/20000) / 32767.0 * fHeight / 2 + fHeight / 2);
|
||||
sumCount = 0;
|
||||
sum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
printf("finished computing\n");
|
||||
|
||||
/* rendering */
|
||||
RenderBitmap();
|
||||
|
||||
/* ask drawing */
|
||||
|
||||
fIsRendering = false;
|
||||
|
||||
if (Window()->LockWithTimeout(5000) == B_OK) {
|
||||
Invalidate();
|
||||
Window()->Unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopeView::SetMainTime(bigtime_t timestamp)
|
||||
{
|
||||
fMainTime = timestamp;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopeView::SetTotalTime(bigtime_t timestamp)
|
||||
{
|
||||
fTotalTime = timestamp;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void
|
||||
ScopeView::SetLeftTime(bigtime_t timestamp)
|
||||
{
|
||||
fLeftTime = timestamp;
|
||||
RenderBitmap();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void
|
||||
ScopeView::SetRightTime(bigtime_t timestamp)
|
||||
{
|
||||
fRightTime = timestamp;
|
||||
RenderBitmap();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopeView::RenderTrack(BMediaTrack *track, media_format format)
|
||||
{
|
||||
fMediaTrack = track;
|
||||
fPlayFormat = format;
|
||||
release_sem(fRenderSem);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopeView::FrameResized(float width, float height)
|
||||
{
|
||||
InitBitmap();
|
||||
RenderBitmap();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
rect.right -= 2;
|
||||
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();
|
||||
|
||||
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]);
|
||||
//printf("point x %f y %f\n", point.x, point.y);
|
||||
fBitmapView->StrokeLine(point, point);
|
||||
}
|
||||
|
||||
fBitmap->Unlock();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#ifndef SCOPEVIEW_H
|
||||
#define SCOPEVIEW_H
|
||||
|
||||
#include <Bitmap.h>
|
||||
#include <View.h>
|
||||
#include <MediaTrack.h>
|
||||
|
||||
|
||||
class ScopeView : public BView
|
||||
{
|
||||
public:
|
||||
ScopeView(BRect rect, uint32 resizeFlags);
|
||||
~ScopeView();
|
||||
void AttachedToWindow();
|
||||
void DetachedFromWindow();
|
||||
void Draw(BRect updateRect);
|
||||
void SetMainTime(bigtime_t timestamp);
|
||||
void SetLeftTime(bigtime_t timestamp);
|
||||
void SetRightTime(bigtime_t timestamp);
|
||||
void SetTotalTime(bigtime_t timestamp);
|
||||
void RenderTrack(BMediaTrack *track, media_format format);
|
||||
virtual void FrameResized(float width, float height);
|
||||
private:
|
||||
void Run();
|
||||
void Quit();
|
||||
static int32 RenderLaunch(void *data);
|
||||
void RenderLoop();
|
||||
void RenderBitmap();
|
||||
void InitBitmap();
|
||||
|
||||
thread_id fThreadId;
|
||||
BBitmap *fBitmap;
|
||||
BView *fBitmapView;
|
||||
sem_id fRenderSem;
|
||||
bool fIsRendering;
|
||||
BMediaTrack *fMediaTrack;
|
||||
media_format fPlayFormat;
|
||||
|
||||
bool fQuitting;
|
||||
bigtime_t fMainTime;
|
||||
bigtime_t fRightTime;
|
||||
bigtime_t fLeftTime;
|
||||
bigtime_t fTotalTime;
|
||||
int32 fPreview[20000];
|
||||
float fHeight;
|
||||
};
|
||||
|
||||
#endif /* SCOPEVIEW_H */
|
||||
@@ -0,0 +1,706 @@
|
||||
/*******************************************************************************
|
||||
/
|
||||
/ File: SoundConsumer.cpp
|
||||
/
|
||||
/ Description: Record sound from some sound-producing Node.
|
||||
/
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <OS.h>
|
||||
#include <scheduler.h>
|
||||
#include <Buffer.h>
|
||||
#include <TimeSource.h>
|
||||
|
||||
#include "SoundPrivate.h"
|
||||
#include "SoundConsumer.h"
|
||||
#include "array_delete.h"
|
||||
|
||||
|
||||
// If we don't mind the format changing to another format while
|
||||
// running, we can define this to 1. Look for the symbol down in the source.
|
||||
#define ACCEPT_ANY_FORMAT_CHANGE 0
|
||||
|
||||
|
||||
// Compiling with NDEBUG means "release" -- it also turns off assert() and
|
||||
// other such debugging aids. By contrast, DEBUG turns on extra debugging
|
||||
// in some programs.
|
||||
#if !NDEBUG
|
||||
#define FPRINTF fprintf
|
||||
#else
|
||||
#define FPRINTF (void)
|
||||
#endif
|
||||
|
||||
// Comment out the FPRINTF part of these lines to reduce verbiage.
|
||||
// Enabling MESSAGE will kill performance on slower machines, because it
|
||||
// prints for each message received (including each buffer).
|
||||
#define NODE //FPRINTF
|
||||
#define MESSAGE //FPRINTF
|
||||
|
||||
|
||||
SoundConsumer::SoundConsumer(
|
||||
const char * name,
|
||||
SoundProcessFunc recordFunc,
|
||||
SoundNotifyFunc notifyFunc,
|
||||
void * cookie) :
|
||||
BMediaNode(name ? name : "SoundConsumer"),
|
||||
BBufferConsumer(B_MEDIA_RAW_AUDIO)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer::SoundConsumer(%p, %p, %p, %p)\n",
|
||||
name, recordFunc, notifyFunc, cookie);
|
||||
|
||||
if (!name) name = "SoundConsumer";
|
||||
|
||||
// Set up the hook functions.
|
||||
m_recordHook = recordFunc;
|
||||
m_notifyHook = notifyFunc;
|
||||
m_cookie = cookie;
|
||||
|
||||
// Create the port that we publish as our Control Port.
|
||||
char pname[32];
|
||||
sprintf(pname, "%.20s Control", name);
|
||||
m_port = create_port(10, pname);
|
||||
|
||||
// Initialize our single media_input. Make sure it knows
|
||||
// the Control Port associated with the destination, and
|
||||
// the index of the destination (since we only have one,
|
||||
// that's trivial).
|
||||
m_input.destination.port = m_port;
|
||||
m_input.destination.id = 1;
|
||||
sprintf(m_input.name, "%.20s Input", name);
|
||||
|
||||
// Set up the timing variables that we'll be using.
|
||||
m_trTimeout = 0LL;
|
||||
m_tpSeekAt = 0;
|
||||
m_tmSeekTo = 0;
|
||||
m_delta = 0;
|
||||
m_seeking = false;
|
||||
|
||||
// Create, and run, the thread that we use to service
|
||||
// the Control Port.
|
||||
sprintf(pname, "%.20s Service", name);
|
||||
m_thread = spawn_thread(ThreadEntry, pname, 110, this);
|
||||
resume_thread(m_thread);
|
||||
}
|
||||
|
||||
|
||||
SoundConsumer::~SoundConsumer()
|
||||
{
|
||||
NODE(stderr, "SoundConsumer::~SoundConsumer()\n");
|
||||
// Signal to our thread that it's time to go home.
|
||||
write_port(m_port, MSG_QUIT_NOW, 0, 0);
|
||||
status_t s;
|
||||
while (wait_for_thread(m_thread, &s) == B_INTERRUPTED)
|
||||
NODE(stderr, "wait_for_thread() B_INTERRUPTED\n");
|
||||
delete_port(m_port);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SoundConsumer::SetHooks(
|
||||
SoundProcessFunc recordFunc,
|
||||
SoundNotifyFunc notifyFunc,
|
||||
void * cookie)
|
||||
{
|
||||
// SetHooks needs to be synchronized with the service thread, else we may
|
||||
// call the wrong hook function with the wrong cookie, which would be bad.
|
||||
// Rather than do locking, which is expensive, we streamline the process
|
||||
// by sending our service thread a request to change the hooks, and waiting
|
||||
// for the acknowledge.
|
||||
status_t err = B_OK;
|
||||
set_hooks_q cmd;
|
||||
cmd.process = recordFunc;
|
||||
cmd.notify = notifyFunc;
|
||||
cmd.cookie = cookie;
|
||||
// If we're not in the service thread, we need to round-trip a message.
|
||||
if (find_thread(0) != m_thread) {
|
||||
cmd.reply = create_port(1, "SetHooks reply");
|
||||
// Send the private message to our service thread.
|
||||
err = write_port(ControlPort(), MSG_CHANGE_HOOKS, &cmd, sizeof(cmd));
|
||||
if (err >= 0) {
|
||||
int32 code;
|
||||
// Wait for acknowledge from the service thread.
|
||||
err = read_port_etc(cmd.reply, &code, 0, 0, B_TIMEOUT, 6000000LL);
|
||||
if (err > 0) err = 0;
|
||||
NODE(stderr, "SoundConsumer::SetHooks read reply: %#010lx\n", err);
|
||||
}
|
||||
// Clean up.
|
||||
delete_port(cmd.reply);
|
||||
}
|
||||
else {
|
||||
// Within the service thread, it's OK to just go ahead and do the change.
|
||||
DoHookChange(&cmd);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// BMediaNode-derived methods
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
port_id SoundConsumer::ControlPort() const
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
|
||||
BMediaAddOn* SoundConsumer::AddOn(
|
||||
int32 * internal_id) const
|
||||
{
|
||||
// This object is instantiated inside an application.
|
||||
// Therefore, it has no add-on.
|
||||
if (internal_id) *internal_id = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::Start(
|
||||
bigtime_t performance_time)
|
||||
{
|
||||
// Since we are a consumer and just blindly accept buffers that are
|
||||
// thrown at us, we don't need to do anything special in Start()/Stop().
|
||||
// If we were (also) a producer, we'd have to be more elaborate.
|
||||
// The only thing we do is immediately perform any queued Seek based on
|
||||
// the start time, which is the right thing to do (seeing as we were
|
||||
// Seek()-ed when we weren't started).
|
||||
if (m_seeking) {
|
||||
m_delta = performance_time - m_tmSeekTo;
|
||||
m_seeking = false;
|
||||
}
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_WILL_START, performance_time);
|
||||
}
|
||||
else {
|
||||
Notify(B_WILL_START, performance_time);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::Stop(
|
||||
bigtime_t performance_time,
|
||||
bool immediate)
|
||||
{
|
||||
// Since we are a consumer and just blindly accept buffers that are
|
||||
// thrown at us, we don't need to do anything special in Start()/Stop().
|
||||
// If we were (also) a producer, we'd have to be more elaborate.
|
||||
// Note that this is not strictly in conformance with The Rules,
|
||||
// but since this is not an add-on Node for use with any application;
|
||||
// it's a Node over which we have complete control, we can live with
|
||||
// treating buffers received before the start time or after the stop
|
||||
// time as any other buffer.
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_WILL_STOP, performance_time, immediate);
|
||||
}
|
||||
else {
|
||||
Notify(B_WILL_STOP, performance_time, immediate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::Seek(
|
||||
bigtime_t media_time,
|
||||
bigtime_t performance_time)
|
||||
{
|
||||
// Seek() on a consumer just serves to offset the time stamp
|
||||
// of received buffers passed to our Record hook function.
|
||||
// In the hook function, you may wish to save those time stamps
|
||||
// to disk or otherwise store them. You may also want to
|
||||
// synchronize this node's media time with an upstream
|
||||
// producer's media time to make this offset meaningful.
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_WILL_SEEK, performance_time, media_time);
|
||||
}
|
||||
else {
|
||||
Notify(B_WILL_SEEK, performance_time, media_time);
|
||||
}
|
||||
m_tpSeekAt = performance_time;
|
||||
m_tmSeekTo = media_time;
|
||||
m_seeking = true;
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::SetRunMode(
|
||||
run_mode mode)
|
||||
{
|
||||
if (mode == BMediaNode::B_OFFLINE) {
|
||||
// BMediaNode::B_OFFLINE means we don't need to run in
|
||||
// real time. So, we shouldn't run as a real time
|
||||
// thread.
|
||||
int32 new_prio = suggest_thread_priority(B_OFFLINE_PROCESSING);
|
||||
set_thread_priority(m_thread, new_prio);
|
||||
}
|
||||
else {
|
||||
// We're running in real time, so we'd better have
|
||||
// a big enough thread priority to handle it!
|
||||
// Here's where those magic scheduler values
|
||||
// come from:
|
||||
//
|
||||
// * In the worst case, we process one buffer per
|
||||
// reschedule (we get rescheduled when we go to
|
||||
// look for a message on our Control Port), so
|
||||
// in order to keep up with the incoming buffers,
|
||||
// the duration of one buffer becomes our
|
||||
// scheduling period. If we don't know anything
|
||||
// about the buffers, we pick a reasonable
|
||||
// default.
|
||||
// * We're a simple consumer, so we don't have to
|
||||
// be too picky about the jitter. Half a period
|
||||
// of jitter means that we'd have to get two
|
||||
// consecutive worst-case reschedules before
|
||||
// we'd fall behind.
|
||||
// * The amount of time we spend processing is
|
||||
// our ProcessingLatency().
|
||||
bigtime_t period = 10000;
|
||||
if (buffer_duration(m_input.format.u.raw_audio) > 0) {
|
||||
period = buffer_duration(m_input.format.u.raw_audio);
|
||||
}
|
||||
// assuming we're running for 500 us or less per buffer
|
||||
int32 new_prio = suggest_thread_priority(B_AUDIO_RECORDING,
|
||||
period, period/2, ProcessingLatency());
|
||||
set_thread_priority(m_thread, new_prio);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::TimeWarp(
|
||||
bigtime_t at_real_time,
|
||||
bigtime_t to_performance_time)
|
||||
{
|
||||
// Since buffers will come pre-time-stamped, we only need to look
|
||||
// at them, so we can ignore the time warp as a consumer.
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_WILL_TIMEWARP, at_real_time, to_performance_time);
|
||||
}
|
||||
else {
|
||||
Notify(B_WILL_TIMEWARP, at_real_time, to_performance_time);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::Preroll()
|
||||
{
|
||||
// There is nothing for us to do in Preroll()
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::SetTimeSource(
|
||||
BTimeSource * /* time_source */)
|
||||
{
|
||||
// We don't need to do anything special to take note of the
|
||||
// fact that the time source changed, because we get our timing
|
||||
// information from the buffers we receive.
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::HandleMessage(
|
||||
int32 message,
|
||||
const void * data,
|
||||
size_t size)
|
||||
{
|
||||
// Check with each of our superclasses to see if they
|
||||
// understand the message. If none of them do, call
|
||||
// BMediaNode::HandleBadMessage().
|
||||
if ((BBufferConsumer::HandleMessage(message, data, size) < 0)
|
||||
&& (BMediaNode::HandleMessage(message, data, size) < 0))
|
||||
{
|
||||
HandleBadMessage(message, data, size);
|
||||
return B_ERROR;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// BBufferConsumer-derived methods
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
status_t SoundConsumer::AcceptFormat(
|
||||
const media_destination & dest,
|
||||
media_format * format)
|
||||
{
|
||||
// We only accept formats aimed at our single input.
|
||||
if (dest != m_input.destination) {
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
// If no format is specified, we say we want raw audio.
|
||||
if (format->type <= 0) {
|
||||
format->type = B_MEDIA_RAW_AUDIO;
|
||||
format->u.raw_audio = media_raw_audio_format::wildcard;
|
||||
}
|
||||
// If a non-raw-audio format is specified, we tell the world what
|
||||
// we want, and that the specified format was unacceptable to us.
|
||||
else if (format->type != B_MEDIA_RAW_AUDIO) {
|
||||
format->type = B_MEDIA_RAW_AUDIO;
|
||||
format->u.raw_audio = media_raw_audio_format::wildcard;
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
#if !ACCEPT_ANY_FORMAT_CHANGE
|
||||
// If we're already connected, and this format doesn't go with the
|
||||
// format in effect, we dont' accept this new format.
|
||||
if (!format_is_compatible(*format, m_input.format)) {
|
||||
*format = m_input.format;
|
||||
return B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
#endif
|
||||
// I guess we're OK by now, because we have no particular needs as
|
||||
// far as frame rate, sample format, etc go.
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::GetNextInput(
|
||||
int32 * cookie,
|
||||
media_input * out_input)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer: GetNextInput()\n");
|
||||
// The "next" is kind of misleading, since it's also used for
|
||||
// getting the first (and only) input.
|
||||
if (!*cookie) {
|
||||
if (m_input.source == media_source::null) {
|
||||
// If there's no current connection, make sure we return a
|
||||
// reasonable format telling the world we accept any raw audio.
|
||||
m_input.format.type = B_MEDIA_RAW_AUDIO;
|
||||
m_input.format.u.raw_audio = media_raw_audio_format::wildcard;
|
||||
m_input.node = Node();
|
||||
m_input.destination.port = ControlPort();
|
||||
m_input.destination.id = 1;
|
||||
}
|
||||
*out_input = m_input;
|
||||
*cookie = 1;
|
||||
return B_OK;
|
||||
}
|
||||
// There's only one input.
|
||||
return B_BAD_INDEX;
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::DisposeInputCookie(
|
||||
int32 /* cookie */)
|
||||
{
|
||||
// We didn't allocate any memory or set any state in GetNextInput()
|
||||
// so this function is a no-op.
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::BufferReceived(
|
||||
BBuffer * buffer)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer::BufferReceived()\n");
|
||||
// Whee, a buffer! Update the seek info, if necessary.
|
||||
if (m_seeking && buffer->Header()->start_time >= m_tpSeekAt) {
|
||||
m_delta = m_tpSeekAt - m_tmSeekTo;
|
||||
m_seeking = false;
|
||||
}
|
||||
// If there is a record hook, let the interested party have at it!
|
||||
if (m_recordHook) {
|
||||
(*m_recordHook)(m_cookie, buffer->Header()->start_time-m_delta, buffer->Data(), buffer->Header()->size_used, m_input.format.u.raw_audio);
|
||||
}
|
||||
else {
|
||||
Record(buffer->Header()->start_time-m_delta, buffer->Data(), buffer->Header()->size_used, m_input.format.u.raw_audio);
|
||||
}
|
||||
// Buffers should ALWAYS be recycled, else whomever is producing them
|
||||
// will starve.
|
||||
buffer->Recycle();
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::ProducerDataStatus(
|
||||
const media_destination & for_whom,
|
||||
int32 status,
|
||||
bigtime_t at_media_time)
|
||||
{
|
||||
if (for_whom == m_input.destination) {
|
||||
// Tell whomever is interested that the upstream producer will or won't
|
||||
// send more data in the immediate future.
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_PRODUCER_DATA_STATUS, status, at_media_time);
|
||||
}
|
||||
else {
|
||||
Notify(B_PRODUCER_DATA_STATUS, status, at_media_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::GetLatencyFor(
|
||||
const media_destination & for_whom,
|
||||
bigtime_t * out_latency,
|
||||
media_node_id * out_timesource)
|
||||
{
|
||||
// We only accept requests for the one-and-only input of our Node.
|
||||
if (for_whom != m_input.destination) {
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
// Tell the world about our latency information (overridable by user).
|
||||
*out_latency = TotalLatency();
|
||||
*out_timesource = TimeSource()->Node().node;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::Connected(
|
||||
const media_source & producer,
|
||||
const media_destination & where,
|
||||
const media_format & with_format,
|
||||
media_input * out_input)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer::Connected()\n");
|
||||
// Only accept connection requests when we're not already connected.
|
||||
if (m_input.source != media_source::null) {
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
// Only accept connection requests on the one-and-only available input.
|
||||
if (where != m_input.destination) {
|
||||
return B_MEDIA_BAD_DESTINATION;
|
||||
}
|
||||
// Other than that, we accept pretty much anything. The format has been
|
||||
// pre-cleared through AcceptFormat(), and we accept any format anyway.
|
||||
m_input.source = producer;
|
||||
m_input.format = with_format;
|
||||
// Tell whomever is interested that there's now a connection.
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_CONNECTED, m_input.name);
|
||||
}
|
||||
else {
|
||||
Notify(B_CONNECTED, m_input.name);
|
||||
}
|
||||
// This is the most important line -- return our connection information
|
||||
// to the world so it can use it!
|
||||
*out_input = m_input;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void SoundConsumer::Disconnected(
|
||||
const media_source & producer,
|
||||
const media_destination & where)
|
||||
{
|
||||
// We can't disconnect something which isn't us.
|
||||
if (where != m_input.destination) {
|
||||
return;
|
||||
}
|
||||
// We can't disconnect from someone who isn't connected to us.
|
||||
if (producer != m_input.source) {
|
||||
return;
|
||||
}
|
||||
// Tell the interested party that it's time to leave.
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_DISCONNECTED);
|
||||
}
|
||||
else {
|
||||
Notify(B_DISCONNECTED);
|
||||
}
|
||||
// Mark ourselves as not-connected.
|
||||
m_input.source = media_source::null;
|
||||
}
|
||||
|
||||
|
||||
status_t SoundConsumer::FormatChanged(
|
||||
const media_source & producer,
|
||||
const media_destination & consumer,
|
||||
int32 from_change_count,
|
||||
const media_format & format)
|
||||
{
|
||||
NODE(stderr, "SoundConsumer::Connected()\n");
|
||||
// The up-stream guy feels like changing the format. If we can accept
|
||||
// arbitrary format changes, we just say "OK". If, however, we're recording
|
||||
// to a file, that's not such a good idea; we only accept format changes
|
||||
// that are compatible with the format we're already using. You set this
|
||||
// behaviour at compile time by defining ACCEPT_ANY_FORMAT_CHANGE to 1 or 0.
|
||||
status_t err = B_OK;
|
||||
#if ACCEPT_ANY_FORMAT_CHANGE
|
||||
media_format fmt(format);
|
||||
err = AcceptFormat(m_input.destination, &fmt);
|
||||
#else
|
||||
if (m_input.source != media_source::null) {
|
||||
err = format_is_compatible(format, m_input.format) ? B_OK : B_MEDIA_BAD_FORMAT;
|
||||
}
|
||||
#endif
|
||||
if (err >= B_OK) {
|
||||
m_input.format = format;
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_FORMAT_CHANGED, &m_input.format.u.raw_audio);
|
||||
}
|
||||
else {
|
||||
Notify(B_FORMAT_CHANGED, &m_input.format.u.raw_audio);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundConsumer::DoHookChange(
|
||||
void * msg)
|
||||
{
|
||||
// Tell the old guy we're changing the hooks ...
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_HOOKS_CHANGED);
|
||||
}
|
||||
else {
|
||||
Notify(B_HOOKS_CHANGED);
|
||||
}
|
||||
// ... and then do it.
|
||||
set_hooks_q * ptr = (set_hooks_q *)msg;
|
||||
m_recordHook = ptr->process;
|
||||
m_notifyHook = ptr->notify;
|
||||
m_cookie = ptr->cookie;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SoundConsumer::ThreadEntry(
|
||||
void * obj)
|
||||
{
|
||||
((SoundConsumer *)obj)->ServiceThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundConsumer::ServiceThread()
|
||||
{
|
||||
// The Big Bad ServiceThread receives messages aimed at this
|
||||
// Node and dispatches them (typically to HandleMessage()).
|
||||
// If we were a Producer, we might have to do finicky timing and
|
||||
// queued Start()/Stop() processing in here. But we ain't.
|
||||
|
||||
// A media kit message will never be bigger than B_MEDIA_MESSAGE_SIZE.
|
||||
// Avoid wasing stack space by dynamically allocating at start.
|
||||
char * msg = new char[B_MEDIA_MESSAGE_SIZE];
|
||||
// Make sure we clean up this data when we exit the function.
|
||||
array_delete<char> msg_delete(msg);
|
||||
int bad = 0;
|
||||
while (true) {
|
||||
// Call read_port_etc() with a timeout derived from a virtual function,
|
||||
// to allow clients to do special processing if necessary.
|
||||
bigtime_t timeout = Timeout();
|
||||
int32 code = 0;
|
||||
status_t err = read_port_etc(m_port, &code, msg, B_MEDIA_MESSAGE_SIZE,
|
||||
B_TIMEOUT, timeout);
|
||||
MESSAGE(stderr, "SoundConsumer::ServiceThread() port %ld message %#010lx\n", m_port, code);
|
||||
// If we received a message, err will be the size of the message (including 0).
|
||||
if (err >= 0) {
|
||||
// Real messages reset the timeout time.
|
||||
m_trTimeout = 0;
|
||||
bad = 0;
|
||||
// Check for our private stop message.
|
||||
if (code == MSG_QUIT_NOW) {
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_NODE_DIES, 0);
|
||||
}
|
||||
else {
|
||||
Notify(B_NODE_DIES, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Else check for our private change-hooks message.
|
||||
else if (code == MSG_CHANGE_HOOKS) {
|
||||
DoHookChange(msg);
|
||||
// Write acknowledge to waiting thread.
|
||||
write_port(((set_hooks_q *)msg)->reply, 0, 0, 0);
|
||||
}
|
||||
// Else it has to be a regular media kit message; go ahead and
|
||||
// dispatch it.
|
||||
else {
|
||||
HandleMessage(code, msg, err);
|
||||
}
|
||||
}
|
||||
// Timing out means that there was no buffer. Tell the interested party.
|
||||
else if (err == B_TIMED_OUT) {
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_OP_TIMED_OUT, timeout);
|
||||
}
|
||||
else {
|
||||
Notify(B_OP_TIMED_OUT, timeout);
|
||||
}
|
||||
}
|
||||
// Other errors are bad.
|
||||
else {
|
||||
FPRINTF(stderr, "SoundConsumer: error %#010lx\n", err);
|
||||
bad++;
|
||||
// If we receive three bad reads with no good messages inbetween,
|
||||
// things are probably not going to improve (like the port disappeared
|
||||
// or something) so we call it a day.
|
||||
if (bad > 3) {
|
||||
if (m_notifyHook) {
|
||||
(*m_notifyHook)(m_cookie, B_NODE_DIES, bad, err, code, msg);
|
||||
}
|
||||
else {
|
||||
Notify(B_NODE_DIES, bad, err, code, msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
SoundConsumer::Timeout()
|
||||
{
|
||||
// Timeout() is called for each call to read_port_etc() in the service
|
||||
// thread to figure out a reasonable time-out value. The default behaviour
|
||||
// we've picked is to exponentially back off from one second and upwards.
|
||||
// While it's true that 44 back-offs will run us out of precision in a
|
||||
// bigtime_t, the time to actually reach 44 consecutive back-offs is longer
|
||||
// than the expected market longevity of just about any piece of real estate.
|
||||
// Is that the sound of an impending year-fifteen-million software problem? :-)
|
||||
m_trTimeout = (m_trTimeout < 1000000) ? 1000000 : m_trTimeout*2;
|
||||
return m_trTimeout;
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
SoundConsumer::ProcessingLatency()
|
||||
{
|
||||
// We're saying it takes us 500 us to process each buffer. If all we do is
|
||||
// copy the data, it probably takes much less than that, but it doesn't
|
||||
// hurt to be slightly conservative.
|
||||
return 500LL;
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
SoundConsumer::TotalLatency()
|
||||
{
|
||||
// Had we been a producer that passes buffers on, we'd have to
|
||||
// include downstream latency in this value. But we are not.
|
||||
return ProcessingLatency();
|
||||
}
|
||||
|
||||
void
|
||||
SoundConsumer::Record(
|
||||
bigtime_t /* time */,
|
||||
const void * /* data */,
|
||||
size_t /* size */,
|
||||
const media_raw_audio_format & /* format */)
|
||||
{
|
||||
// If there is no record hook installed, we instead call this function
|
||||
// for received buffers.
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundConsumer::Notify(
|
||||
int32 /* cause */,
|
||||
...)
|
||||
{
|
||||
// If there is no notification hook installed, we instead call this function
|
||||
// for giving notification of various events.
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*******************************************************************************
|
||||
/
|
||||
/ File: SoundConsumer.h
|
||||
/
|
||||
/ Description: Record sound from some sound-producing Node.
|
||||
/
|
||||
/ Copyright 1998, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#if !defined( _SoundConsumer_h )
|
||||
#define _SoundConsumer_h
|
||||
|
||||
#include <BufferConsumer.h>
|
||||
#include "SoundUtils.h"
|
||||
|
||||
// To use this Consumer:
|
||||
// 1. Create Record and Notify hooks, or subclass SoundConsumer
|
||||
// if you'd rather use the inheritance hierarchy.
|
||||
// * The Record function should do whatever you want to do
|
||||
// when you receive a buffer.
|
||||
// * The Notify function should handle whatever events
|
||||
// you wish to handle (defined in SoundUtil.h).
|
||||
// 2: Create an instance of SoundConsumer, giving it the
|
||||
// appropriate hook functions. Or, create an instance of an
|
||||
// appropriate subclass if you've made one.
|
||||
// 3: Register your new Consumer with the MediaRoster.
|
||||
// 4: Connect your Consumer to some Producer.
|
||||
// 5: Start or Stop the Consumer if your hook functions
|
||||
// implement behavior for these kinds of events.
|
||||
// Seek the Consumer to set the offset of the timestamps that
|
||||
// your Record function will see.
|
||||
// 6: When you're done, disconnect the Consumer, then delete it.
|
||||
|
||||
class SoundConsumer :
|
||||
public BBufferConsumer
|
||||
{
|
||||
public:
|
||||
SoundConsumer(
|
||||
const char * name,
|
||||
SoundProcessFunc recordFunc = NULL,
|
||||
SoundNotifyFunc notifyFunc = NULL,
|
||||
void * cookie = NULL);
|
||||
~SoundConsumer();
|
||||
|
||||
// This function is OK to call from any thread.
|
||||
status_t SetHooks(
|
||||
SoundProcessFunc recordFunc = NULL,
|
||||
SoundNotifyFunc notifyFunc = NULL,
|
||||
void * cookie = NULL);
|
||||
|
||||
// The MediaNode interface
|
||||
public:
|
||||
virtual port_id ControlPort() const;
|
||||
virtual BMediaAddOn* AddOn(
|
||||
int32 * internal_id) const; /* Who instantiated you -- or NULL for app class */
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual void Start(
|
||||
bigtime_t performance_time);
|
||||
virtual void Stop(
|
||||
bigtime_t performance_time,
|
||||
bool immediate);
|
||||
virtual void Seek(
|
||||
bigtime_t media_time,
|
||||
bigtime_t performance_time);
|
||||
virtual void SetRunMode(
|
||||
run_mode mode);
|
||||
virtual void TimeWarp(
|
||||
bigtime_t at_real_time,
|
||||
bigtime_t to_performance_time);
|
||||
virtual void Preroll();
|
||||
virtual void SetTimeSource(
|
||||
BTimeSource * time_source);
|
||||
virtual status_t HandleMessage(
|
||||
int32 message,
|
||||
const void * data,
|
||||
size_t size);
|
||||
|
||||
// The BufferConsumer interface
|
||||
virtual status_t AcceptFormat(
|
||||
const media_destination & dest,
|
||||
media_format * format);
|
||||
virtual status_t GetNextInput( /* cookie starts at 0 */
|
||||
int32 * cookie,
|
||||
media_input * out_input);
|
||||
virtual void DisposeInputCookie(
|
||||
int32 cookie);
|
||||
virtual void BufferReceived(
|
||||
BBuffer * buffer);
|
||||
virtual void ProducerDataStatus(
|
||||
const media_destination & for_whom,
|
||||
int32 status,
|
||||
bigtime_t at_media_time);
|
||||
virtual status_t GetLatencyFor(
|
||||
const media_destination & for_whom,
|
||||
bigtime_t * out_latency,
|
||||
media_node_id * out_timesource);
|
||||
virtual status_t Connected(
|
||||
const media_source & producer,
|
||||
const media_destination & where,
|
||||
const media_format & with_format,
|
||||
media_input * out_input);
|
||||
virtual void Disconnected(
|
||||
const media_source & producer,
|
||||
const media_destination & where);
|
||||
virtual status_t FormatChanged(
|
||||
const media_source & producer,
|
||||
const media_destination & consumer,
|
||||
int32 from_change_count,
|
||||
const media_format & format);
|
||||
|
||||
protected:
|
||||
// Functions called when no hooks are installed.
|
||||
// OK to override instead of installing hooks.
|
||||
virtual void Record(
|
||||
bigtime_t time,
|
||||
const void * data,
|
||||
size_t size,
|
||||
const media_raw_audio_format & format);
|
||||
virtual void Notify(
|
||||
int32 cause,
|
||||
...);
|
||||
|
||||
private:
|
||||
SoundProcessFunc m_recordHook;
|
||||
SoundNotifyFunc m_notifyHook;
|
||||
void * m_cookie;
|
||||
media_input m_input;
|
||||
thread_id m_thread;
|
||||
port_id m_port;
|
||||
|
||||
// The times we need to deal with
|
||||
// My notation for times: tr = real time,
|
||||
// tp = performance time, tm = media time.
|
||||
bigtime_t m_trTimeout; // how long to wait on the input port
|
||||
bigtime_t m_tpSeekAt; // when we Seek
|
||||
bigtime_t m_tmSeekTo; // target time for Seek
|
||||
|
||||
// The transformation from media to peformance time.
|
||||
// d = p - m, so m + d = p.
|
||||
// Media time is generally governed by the Seek
|
||||
// function. In our node, we simply use media time as
|
||||
// the time that we report to the record hook function.
|
||||
// If we were a producer node, we might use media time
|
||||
// to track where we were in playing a certain piece
|
||||
// of media. But we aren't.
|
||||
bigtime_t m_delta;
|
||||
|
||||
// State variables
|
||||
bool m_seeking; // a Seek is pending
|
||||
|
||||
// Functions to calculate timing values. OK to override.
|
||||
// ProcessingLatency is the time it takes to process a buffer;
|
||||
// TotalLatency is returned to producer; Timeout is passed
|
||||
// to call to read_port_etc() in service thread loop.
|
||||
virtual bigtime_t Timeout();
|
||||
virtual bigtime_t ProcessingLatency();
|
||||
virtual bigtime_t TotalLatency();
|
||||
// The actual thread doing the work
|
||||
static status_t ThreadEntry(
|
||||
void * obj);
|
||||
void ServiceThread();
|
||||
void DoHookChange(
|
||||
void * msg);
|
||||
};
|
||||
|
||||
|
||||
#endif /* _SoundConsumer_h */
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 <Entry.h>
|
||||
|
||||
#include "SoundListView.h"
|
||||
|
||||
|
||||
SoundListView::SoundListView(
|
||||
const BRect & area,
|
||||
const char * name,
|
||||
uint32 resize) :
|
||||
BListView(area, name, B_SINGLE_SELECTION_LIST, resize)
|
||||
{
|
||||
}
|
||||
|
||||
SoundListView::~SoundListView()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SoundListView::Draw(BRect updateRect)
|
||||
{
|
||||
if (IsEmpty()) {
|
||||
SetHighColor(235,235,235);
|
||||
FillRect(Bounds());
|
||||
|
||||
SetHighColor(0,0,0);
|
||||
BFont font(be_bold_font);
|
||||
font.SetSize(12.0);
|
||||
SetFont(&font);
|
||||
font_height height;
|
||||
font.GetHeight(&height);
|
||||
float width = font.StringWidth("Drop Files Here");
|
||||
|
||||
BPoint pt;
|
||||
pt.x = (Bounds().Width() - width) / 2;
|
||||
pt.y = (Bounds().Height() + height.ascent + height.descent)/ 2;
|
||||
DrawString("Drop Files Here", pt);
|
||||
}
|
||||
BListView::Draw(updateRect);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundListView::AttachedToWindow()
|
||||
{
|
||||
BListView::AttachedToWindow();
|
||||
SetViewColor(255,255,255);
|
||||
}
|
||||
|
||||
|
||||
SoundListItem::SoundListItem(
|
||||
const BEntry & entry,
|
||||
bool isTemp)
|
||||
: BStringItem(""),
|
||||
fEntry(entry),
|
||||
fIsTemp(isTemp)
|
||||
{
|
||||
char name[256];
|
||||
fEntry.GetName(name);
|
||||
SetText(name);
|
||||
}
|
||||
|
||||
SoundListItem::~SoundListItem()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#ifndef SOUNDLISTVIEW_H
|
||||
#define SOUNDLISTVIEW_H
|
||||
|
||||
#include <ListView.h>
|
||||
|
||||
class SoundListView : public BListView {
|
||||
public:
|
||||
SoundListView(const BRect & area, const char * name, uint32 resize);
|
||||
virtual ~SoundListView();
|
||||
|
||||
virtual void Draw(BRect updateRect);
|
||||
virtual void AttachedToWindow();
|
||||
};
|
||||
|
||||
|
||||
#include <ListItem.h>
|
||||
|
||||
class SoundListItem : public BStringItem {
|
||||
public:
|
||||
SoundListItem(const BEntry & entry, bool isTemp);
|
||||
virtual ~SoundListItem();
|
||||
|
||||
BEntry & Entry() { return fEntry; }
|
||||
bool IsTemp() { return fIsTemp; }
|
||||
void SetTemp(bool isTemp) { fIsTemp = isTemp; }
|
||||
private:
|
||||
BEntry fEntry;
|
||||
bool fIsTemp;
|
||||
};
|
||||
|
||||
|
||||
#endif /* SOUNDLISTVIEW_H */
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*******************************************************************************
|
||||
/
|
||||
/ File: SoundPrivate.h
|
||||
/
|
||||
/ Description: Implementation headers for SoundConsumer and SoundProducer.
|
||||
/
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
#if ! defined( _SoundPrivate_h )
|
||||
#define _SoundPrivate_h
|
||||
|
||||
// The following are implementation details that we don't
|
||||
// want to expose to the world at large.
|
||||
|
||||
#include "SoundUtils.h"
|
||||
|
||||
// This structure is the body of a request that we use to
|
||||
// implement SetHooks().
|
||||
struct set_hooks_q {
|
||||
port_id reply;
|
||||
void * cookie;
|
||||
SoundProcessFunc process;
|
||||
SoundNotifyFunc notify;
|
||||
};
|
||||
|
||||
// All incoming buffers and Media Kit requests arrive at a
|
||||
// media node in the form of messages (which are generally
|
||||
// dispatched for you by your superclasses' HandleMessage
|
||||
// implementations). Each message has a 'type' which is
|
||||
// analagous to a BMessage's 'what' field. We'll define our
|
||||
// own private message types for our SoundConsumer and
|
||||
// SoundProducer to use. The BeOS reserves a range,
|
||||
// 0x60000000 to 0x7fffffff, for us to use.
|
||||
enum {
|
||||
MSG_QUIT_NOW = 0x60000000L,
|
||||
MSG_CHANGE_HOOKS
|
||||
};
|
||||
|
||||
#endif /* _SoundPrivate_h */
|
||||
@@ -0,0 +1,83 @@
|
||||
|
||||
resource app_signature "application/x-vnd.haiku.SoundRecorder";
|
||||
|
||||
resource app_flags B_SINGLE_LAUNCH;
|
||||
|
||||
resource app_version
|
||||
{
|
||||
major = 1,
|
||||
middle = 0,
|
||||
minor = 0,
|
||||
variety = B_APPV_BETA,
|
||||
internal = 0,
|
||||
short_info = "SoundRecorder",
|
||||
long_info = "Haiku Sound Recorder"
|
||||
};
|
||||
|
||||
resource file_types message
|
||||
{
|
||||
"types" = "audio/basic",
|
||||
"types" = "audio/x-wav",
|
||||
"types" = "audio/x-riff",
|
||||
"types" = "audio/x-aifc",
|
||||
"types" = "audio/x-aiff",
|
||||
"types" = "audio/wav",
|
||||
"types" = "audio/aiff",
|
||||
"types" = "audio/riff"
|
||||
};
|
||||
|
||||
resource large_icon array
|
||||
{
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FED8D80202FFFFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF00FED88484D8D80202FFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFF00FED884D8D88484D8D80202FFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFF00FED8D8D88484D8D8AAAAD8D80202FFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFF00FED8D88484D8D8AAAAD8D8AA84D8D80202FFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFF00FED8D884D8D8AAAAD8D8AAAAD8D88484D8D802FFFF"
|
||||
$"FFFFFFFFFFFFFFFFFF00FE07D8D8D88484D8D8AAAAD8D8AA84D8D884D829FFFF"
|
||||
$"FFFFFFFFFFFFFFFF00FE070A0707D8D8D8AAAAD8D8AAAAD8D88484D8D804FFFF"
|
||||
$"FFFFFFFFFFFFFF00FE070A0A0A0A0707D8D8D8AAAAD8D88484D8D8D88329FFFF"
|
||||
$"FFFFFFFFFFFF00FE070A0A0A0A0A0A0A0707D8D8D88484D8D8D8D8838302FFFF"
|
||||
$"FFFFFFFFFF00FE070A0A0A0F150A0A0A0A0A0707D8D8D884D8D883838302FFFF"
|
||||
$"FFFFFFFF00FE070A0A0A0A0F0F0A0A0A0A0A0A0A0707D8D8D8838383830210FF"
|
||||
$"FFFFFF00FED8D807070A0A0A0A0A0A0F150A0A0A0A07D8D88383838383021010"
|
||||
$"FFFF00FED8FEFED8D807070A0A0A0A0F0F0A0A0A07D8D8838383838300101010"
|
||||
$"FF00FED80910D8FEFED8D807070A0A0A0A0A0A07D8D8838383838300101010FF"
|
||||
$"FF00FE091010099CD8FEFED8D807070A0A0A07D8D8838383838300101010FFFF"
|
||||
$"00FE091010099C9C3010D8FEFED8D8070707D8D8838383838300101010FFFFFF"
|
||||
$"00D80C0C099C9C3010100910D8FEFED8D8D8D8838383838300101010FFFFFFFF"
|
||||
$"FF0083832C2C3010100910100910D8FED8D8838383838300101010FFFFFFFFFF"
|
||||
$"FFFF333383830C0C09101009101009D8D8838383838300101010FFFFFFFFFFFF"
|
||||
$"FFFFFFFF330083830C0C09101009D8D8D88383838300101010FFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFF003383830C0C0909D8D8D883838300101010FFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFF0000838309D8D8D883838300101010FFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFF00008383D8D8838300101010FFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFF280083838300101010FFFFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF000000101010FFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
};
|
||||
|
||||
resource mini_icon array
|
||||
{
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFF000000FFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFF00FED8840000FFFFFFFF"
|
||||
$"FFFFFFFFFF00FED8AAD8AA840000FFFF"
|
||||
$"FFFFFFFF00FE0707D88484D8AAD800FF"
|
||||
$"FFFFFF00FE070F090707D8AAD8D800FF"
|
||||
$"FFFF00FE0709150F0F09070FD88300FF"
|
||||
$"FF00FED807070909150F09D883830010"
|
||||
$"00FE09FEFED807070909D88383001010"
|
||||
$"000910099CFEFED807D88383001010FF"
|
||||
$"0010099C9C0C09FED88383001010FFFF"
|
||||
$"FF00092C0C090CD88383001010FFFFFF"
|
||||
$"FFFF0000090CD88383001010FFFFFFFF"
|
||||
$"FFFFFFFF00008383001010FFFFFFFFFF"
|
||||
$"FFFFFFFFFFFF00001010FFFFFFFFFFFF"
|
||||
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
/*******************************************************************************
|
||||
/
|
||||
/ File: SoundUtils.cpp
|
||||
/
|
||||
/ Description: Utility functions for handling audio data.
|
||||
/
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include "SoundUtils.h"
|
||||
|
||||
// These two conversions seem to pop up all the time in media code.
|
||||
// I guess it's the curse of microsecond resolution... ;-)
|
||||
double
|
||||
us_to_s(bigtime_t usecs)
|
||||
{
|
||||
return (usecs / 1000000.0);
|
||||
}
|
||||
|
||||
bigtime_t
|
||||
s_to_us(double secs)
|
||||
{
|
||||
return (bigtime_t) (secs * 1000000.0);
|
||||
}
|
||||
|
||||
int
|
||||
bytes_per_frame(
|
||||
const media_raw_audio_format & format)
|
||||
{
|
||||
// The media_raw_audio_format format constants encode the
|
||||
// bytes-per-sample value in the low nybble. Having a fixed
|
||||
// number of bytes-per-sample, and no inter-sample relationships,
|
||||
// is what makes a format "raw".
|
||||
int bytesPerSample = format.format & 0xf;
|
||||
return bytesPerSample * format.channel_count;
|
||||
}
|
||||
|
||||
int
|
||||
frames_per_buffer(
|
||||
const media_raw_audio_format & format)
|
||||
{
|
||||
// This will give us the number of full-sized frames that will fit
|
||||
// in a buffer. (Remember, integer division automatically rounds
|
||||
// down.)
|
||||
int frames = 0;
|
||||
if (bytes_per_frame(format) > 0) {
|
||||
frames = format.buffer_size / bytes_per_frame(format);
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
bigtime_t
|
||||
buffer_duration(
|
||||
const media_raw_audio_format & format)
|
||||
{
|
||||
// Figuring out duration is easy. We take extra precaution to
|
||||
// not divide by zero or return irrelevant results.
|
||||
bigtime_t duration = 0;
|
||||
if (format.buffer_size > 0 && format.frame_rate > 0 && bytes_per_frame(format) > 0) {
|
||||
// In these kinds of calculations, it's always useful to double-check
|
||||
// the unit conversions. (Anyone remember high school physics?)
|
||||
// bytes/(bytes/frame) / frames/sec
|
||||
// = frames * sec/frames
|
||||
// = secs which is what we want.
|
||||
duration = s_to_us((format.buffer_size / bytes_per_frame(format)) / format.frame_rate);
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
bigtime_t
|
||||
frames_duration(
|
||||
const media_raw_audio_format & format, int64 num_frames)
|
||||
{
|
||||
// Tells us how long in us it will take to produce num_frames,
|
||||
// with the given format.
|
||||
bigtime_t duration = 0;
|
||||
if (format.frame_rate > 0) {
|
||||
duration = s_to_us(num_frames/format.frame_rate);
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
int
|
||||
buffers_for_duration(
|
||||
const media_raw_audio_format & format, bigtime_t duration)
|
||||
{
|
||||
// Double-checking those unit conversions again:
|
||||
// secs * ( (frames/sec) / (frames/buffer) ) = secs * (buffers/sec) = buffers
|
||||
int buffers = 0;
|
||||
if (frames_per_buffer(format) > 0) {
|
||||
buffers = (int) ceil(us_to_s(duration)*(format.frame_rate/frames_per_buffer(format)));
|
||||
}
|
||||
return buffers;
|
||||
}
|
||||
|
||||
int64
|
||||
frames_for_duration(
|
||||
const media_raw_audio_format & format, bigtime_t duration)
|
||||
{
|
||||
return (int64) ceil(format.frame_rate*us_to_s(duration));
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*******************************************************************************
|
||||
/
|
||||
/ File: SoundUtils.h
|
||||
/
|
||||
/ Description: Utility functions for handling audio data.
|
||||
/
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
#if ! defined( _SoundUtils_h )
|
||||
#define _SoundUtils_h
|
||||
|
||||
#include <MediaDefs.h>
|
||||
|
||||
// Simple helper functions that come in handy when doing
|
||||
// buffer calculations.
|
||||
double us_to_s(bigtime_t usecs);
|
||||
bigtime_t s_to_us(double secs);
|
||||
|
||||
int bytes_per_frame(const media_raw_audio_format & format);
|
||||
int frames_per_buffer(const media_raw_audio_format & format);
|
||||
bigtime_t buffer_duration(const media_raw_audio_format & format);
|
||||
bigtime_t frames_duration(const media_raw_audio_format & format,
|
||||
int64 num_frames);
|
||||
int64 frames_for_duration(const media_raw_audio_format & format,
|
||||
bigtime_t duration);
|
||||
int buffers_for_duration(const media_raw_audio_format & format,
|
||||
bigtime_t duration);
|
||||
|
||||
// This is a common hook function interface for
|
||||
// SoundConsumer and SoundProducer to use.
|
||||
typedef void (*SoundProcessFunc)(void * cookie,
|
||||
bigtime_t timestamp, void * data, size_t datasize,
|
||||
const media_raw_audio_format & format);
|
||||
typedef void (*SoundNotifyFunc)(void * cookie,
|
||||
int32 code, ...);
|
||||
|
||||
// These are special codes that we use in the Notify
|
||||
// function hook.
|
||||
enum {
|
||||
B_WILL_START = 1, // performance_time
|
||||
B_WILL_STOP, // performance_time immediate
|
||||
B_WILL_SEEK, // performance_time media_time
|
||||
B_WILL_TIMEWARP, // real_time performance_time
|
||||
B_CONNECTED, // name (char*)
|
||||
B_DISCONNECTED, //
|
||||
B_FORMAT_CHANGED, // media_raw_audio_format*
|
||||
B_NODE_DIES, // node will die!
|
||||
B_HOOKS_CHANGED, //
|
||||
B_OP_TIMED_OUT, // timeout that expired -- Consumer only
|
||||
B_PRODUCER_DATA_STATUS, // status performance_time -- Consumer only
|
||||
B_LATE_NOTICE // how_much performance_time -- Producer only
|
||||
};
|
||||
|
||||
#endif /* _SoundUtils_h */
|
||||
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* 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 "TrackSlider.h"
|
||||
#include "icon_button.h"
|
||||
|
||||
TrackSlider::TrackSlider(BRect rect, const char *title, BMessage *msg, uint32 resizeFlags)
|
||||
: BControl(rect, "slider", NULL, msg, resizeFlags, B_WILL_DRAW | B_FRAME_EVENTS),
|
||||
leftBitmap(BRect(BPoint(0,0), kLeftRightTrackSliderSize), B_CMAP8),
|
||||
rightBitmap(BRect(BPoint(0,0), kLeftRightTrackSliderSize), B_CMAP8),
|
||||
leftThumbBitmap(BRect(0, 0, kLeftRightThumbWidth - 1, kLeftRightThumbHeight - 1), B_CMAP8),
|
||||
rightThumbBitmap(BRect(0, 0, kLeftRightThumbWidth - 1, kLeftRightThumbHeight - 1), B_CMAP8),
|
||||
fLeftTime(0), fRightTime(1000000), fMainTime(0), fTotalTime(1000000),
|
||||
fLeftTracking(false), fRightTracking(false), fMainTracking(false)
|
||||
{
|
||||
fFont.SetSize(8.0);
|
||||
fFont.SetFlags(B_DISABLE_ANTIALIASING);
|
||||
|
||||
int32 numFamilies = count_font_families();
|
||||
for (int32 i = 0; i < numFamilies; i++ ) {
|
||||
font_family family;
|
||||
uint32 flags;
|
||||
if ((get_font_family(i, &family, &flags) == B_OK)
|
||||
&& (strcmp(family, "Baskerville") == 0)) {
|
||||
fFont.SetFamilyAndFace(family, B_REGULAR_FACE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
leftBitmap.SetBits(kLeftTrackSliderBits, kLeftRightTrackSliderWidth * kLeftRightTrackSliderHeight, 0, B_CMAP8);
|
||||
rightBitmap.SetBits(kRightTrackSliderBits, kLeftRightTrackSliderWidth * kLeftRightTrackSliderHeight, 0, B_CMAP8);
|
||||
leftThumbBitmap.SetBits(kLeftThumbBits, kLeftRightThumbWidth * kLeftRightThumbHeight, 0, B_CMAP8);
|
||||
rightThumbBitmap.SetBits(kRightThumbBits, kLeftRightThumbWidth * kLeftRightThumbHeight, 0, B_CMAP8);
|
||||
|
||||
fRight = Bounds().right - kLeftRightTrackSliderWidth;
|
||||
if (fTotalTime == 0) {
|
||||
fLeftX = 14;
|
||||
fRightX = fRight;
|
||||
fPositionX = 15;
|
||||
} else {
|
||||
fLeftX = 14 + (fRight - 15) * ((double)fLeftTime / fTotalTime);
|
||||
fRightX = 15 + (fRight - 16) * ((double)fRightTime / fTotalTime);
|
||||
fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TrackSlider::~TrackSlider()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::AttachedToWindow()
|
||||
{
|
||||
BControl::AttachedToWindow();
|
||||
SetViewColor(B_TRANSPARENT_COLOR);
|
||||
}
|
||||
|
||||
|
||||
#define SLIDER_BASE 10
|
||||
|
||||
void
|
||||
TrackSlider::Draw(BRect updateRect)
|
||||
{
|
||||
SetHighColor(189,186,189);
|
||||
StrokeLine(BPoint(11,SLIDER_BASE+1), BPoint(fRight,SLIDER_BASE+1));
|
||||
SetHighColor(0,0,0);
|
||||
StrokeLine(BPoint(11,SLIDER_BASE+2), BPoint(fRight,SLIDER_BASE+2));
|
||||
SetHighColor(255,255,255);
|
||||
StrokeLine(BPoint(11,SLIDER_BASE+17), BPoint(fRight,SLIDER_BASE+17));
|
||||
SetHighColor(231,227,231);
|
||||
StrokeLine(BPoint(11,SLIDER_BASE+18), BPoint(fRight,SLIDER_BASE+18));
|
||||
|
||||
SetDrawingMode(B_OP_OVER);
|
||||
|
||||
SetHighColor(216,216,216);
|
||||
FillRect(BRect(0,1,fPositionX < 18 ? 18 : fPositionX-26,SLIDER_BASE));
|
||||
FillRect(BRect(fPositionX < 18 ? 65 : fPositionX > fRight - 3 ? fRight : fPositionX+26,0,Bounds().right,SLIDER_BASE));
|
||||
FillRect(BRect(0,0,Bounds().right,0));
|
||||
FillRect(BRect(0,SLIDER_BASE+18,Bounds().right,SLIDER_BASE+21));
|
||||
FillRect(BRect(0,0,10,SLIDER_BASE+21));
|
||||
FillRect(BRect(Bounds().right - 10,0,Bounds().right,SLIDER_BASE+21));
|
||||
|
||||
SetLowColor(HighColor());
|
||||
|
||||
BPoint leftPoint(5,SLIDER_BASE+1);
|
||||
DrawBitmapAsync(&leftBitmap, BRect(BPoint(0,0), kLeftRightTrackSliderSize - BPoint(5,0)),
|
||||
BRect(leftPoint, leftPoint+kLeftRightTrackSliderSize-BPoint(5,0)));
|
||||
BPoint rightPoint(fRight + 1,SLIDER_BASE+1);
|
||||
DrawBitmapAsync(&rightBitmap, BRect(BPoint(5,0), kLeftRightTrackSliderSize),
|
||||
BRect(rightPoint, rightPoint+kLeftRightTrackSliderSize-BPoint(5,0)));
|
||||
|
||||
SetHighColor(153,153,153);
|
||||
FillRect(BRect(11,SLIDER_BASE+3,fLeftX-9,SLIDER_BASE+16));
|
||||
FillRect(BRect(fRightX+9,SLIDER_BASE+3,fRight,SLIDER_BASE+16));
|
||||
if (fLeftX>19) {
|
||||
StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+3),BPoint(fLeftX-6,SLIDER_BASE+3));
|
||||
StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+4),BPoint(fLeftX-7,SLIDER_BASE+4));
|
||||
StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+5),BPoint(fLeftX-8,SLIDER_BASE+5));
|
||||
StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+16),BPoint(fLeftX-6,SLIDER_BASE+16));
|
||||
StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+15),BPoint(fLeftX-7,SLIDER_BASE+15));
|
||||
StrokeLine(BPoint(fLeftX-9,SLIDER_BASE+14),BPoint(fLeftX-8,SLIDER_BASE+14));
|
||||
}
|
||||
if (fRightX < fRight - 5) {
|
||||
StrokeLine(BPoint(fRightX+5,SLIDER_BASE+3),BPoint(fRightX+8,SLIDER_BASE+3));
|
||||
StrokeLine(BPoint(fRightX+7,SLIDER_BASE+4),BPoint(fRightX+8,SLIDER_BASE+4));
|
||||
StrokeLine(BPoint(fRightX+8,SLIDER_BASE+5),BPoint(fRightX+8,SLIDER_BASE+6));
|
||||
StrokeLine(BPoint(fRightX+8,SLIDER_BASE+13),BPoint(fRightX+8,SLIDER_BASE+14));
|
||||
StrokeLine(BPoint(fRightX+5,SLIDER_BASE+16),BPoint(fRightX+8,SLIDER_BASE+16));
|
||||
StrokeLine(BPoint(fRightX+7,SLIDER_BASE+15),BPoint(fRightX+8,SLIDER_BASE+15));
|
||||
}
|
||||
SetHighColor(144,186,136);
|
||||
FillRect(BRect(fLeftX+1,SLIDER_BASE+3,fRightX,SLIDER_BASE+4));
|
||||
FillRect(BRect(fLeftX+1,SLIDER_BASE+5,fLeftX+2,SLIDER_BASE+16));
|
||||
SetHighColor(171,221,161);
|
||||
FillRect(BRect(fLeftX+3,SLIDER_BASE+5,fRightX,SLIDER_BASE+16));
|
||||
|
||||
int i = 17;
|
||||
int j = 18;
|
||||
SetHighColor(128,128,128);
|
||||
for (; i<fLeftX-9; i+=6) {
|
||||
StrokeLine(BPoint(i,SLIDER_BASE+7), BPoint(i,SLIDER_BASE+13));
|
||||
}
|
||||
SetHighColor(179,179,179);
|
||||
for (; j<fLeftX-9; j+=6) {
|
||||
StrokeLine(BPoint(j,SLIDER_BASE+7), BPoint(j,SLIDER_BASE+13));
|
||||
}
|
||||
|
||||
while (i<=fLeftX)
|
||||
i+=6;
|
||||
while (j<=fLeftX)
|
||||
j+=6;
|
||||
|
||||
SetHighColor(144,186,136);
|
||||
for (; i<=fRightX; i+=6) {
|
||||
StrokeLine(BPoint(i,SLIDER_BASE+7), BPoint(i,SLIDER_BASE+13));
|
||||
}
|
||||
SetHighColor(189,244,178);
|
||||
for (; j<=fRightX; j+=6) {
|
||||
StrokeLine(BPoint(j,SLIDER_BASE+7), BPoint(j,SLIDER_BASE+13));
|
||||
}
|
||||
|
||||
while (i<=fRightX+9)
|
||||
i+=6;
|
||||
while (j<=fRightX+9)
|
||||
j+=6;
|
||||
|
||||
SetHighColor(128,128,128);
|
||||
for (; i<=fRight + 1; i+=6) {
|
||||
StrokeLine(BPoint(i,SLIDER_BASE+7), BPoint(i,SLIDER_BASE+13));
|
||||
}
|
||||
SetHighColor(179,179,179);
|
||||
for (; j<=fRight + 1; j+=6) {
|
||||
StrokeLine(BPoint(j,SLIDER_BASE+7), BPoint(j,SLIDER_BASE+13));
|
||||
}
|
||||
|
||||
SetLowColor(HighColor());
|
||||
|
||||
BPoint leftThumbPoint(fLeftX-8,SLIDER_BASE+3);
|
||||
DrawBitmapAsync(&leftThumbBitmap, BRect(BPoint(0,0), kLeftRightThumbSize - BPoint(7,0)),
|
||||
BRect(leftThumbPoint, leftThumbPoint+kLeftRightThumbSize-BPoint(7,0)));
|
||||
|
||||
BPoint rightThumbPoint(fRightX,SLIDER_BASE+3);
|
||||
DrawBitmapAsync(&rightThumbBitmap, BRect(BPoint(6,0), kLeftRightThumbSize),
|
||||
BRect(rightThumbPoint, rightThumbPoint+kLeftRightThumbSize-BPoint(6,0)));
|
||||
|
||||
rgb_color black = {0,0,0};
|
||||
rgb_color rose = {255,152,152};
|
||||
rgb_color red = {255,0,0};
|
||||
rgb_color bordeau = {178,0,0};
|
||||
rgb_color white = {255,255,255};
|
||||
|
||||
DrawCounter(fMainTime, fPositionX, fMainTracking);
|
||||
if (fLeftTracking)
|
||||
DrawCounter(fLeftTime, fLeftX, fLeftTracking);
|
||||
else if (fRightTracking)
|
||||
DrawCounter(fRightTime, fRightX, fRightTracking);
|
||||
|
||||
BeginLineArray(30);
|
||||
AddLine(BPoint(fPositionX,SLIDER_BASE+7), BPoint(fPositionX-4,SLIDER_BASE+3), black);
|
||||
AddLine(BPoint(fPositionX-4,SLIDER_BASE+3), BPoint(fPositionX-4,SLIDER_BASE+1), black);
|
||||
AddLine(BPoint(fPositionX-4,SLIDER_BASE+1), BPoint(fPositionX+4,SLIDER_BASE+1), black);
|
||||
AddLine(BPoint(fPositionX+4,SLIDER_BASE+1), BPoint(fPositionX+4,SLIDER_BASE+3), black);
|
||||
AddLine(BPoint(fPositionX+4,SLIDER_BASE+3), BPoint(fPositionX,SLIDER_BASE+7), black);
|
||||
|
||||
|
||||
AddLine(BPoint(fPositionX-3,SLIDER_BASE+2), BPoint(fPositionX+3,SLIDER_BASE+2), rose);
|
||||
AddLine(BPoint(fPositionX-3,SLIDER_BASE+3), BPoint(fPositionX-1,SLIDER_BASE+5), rose);
|
||||
|
||||
AddLine(BPoint(fPositionX-2,SLIDER_BASE+3), BPoint(fPositionX+2,SLIDER_BASE+3), red);
|
||||
AddLine(BPoint(fPositionX-1,SLIDER_BASE+4), BPoint(fPositionX+1,SLIDER_BASE+4), red);
|
||||
AddLine(BPoint(fPositionX,SLIDER_BASE+5), BPoint(fPositionX,SLIDER_BASE+5), red);
|
||||
|
||||
AddLine(BPoint(fPositionX,SLIDER_BASE+6), BPoint(fPositionX+3,SLIDER_BASE+3), bordeau);
|
||||
|
||||
AddLine(BPoint(fPositionX,SLIDER_BASE+12), BPoint(fPositionX-4,SLIDER_BASE+16), black);
|
||||
AddLine(BPoint(fPositionX-4,SLIDER_BASE+16), BPoint(fPositionX-4,SLIDER_BASE+17), black);
|
||||
AddLine(BPoint(fPositionX-4,SLIDER_BASE+17), BPoint(fPositionX+4,SLIDER_BASE+17), black);
|
||||
AddLine(BPoint(fPositionX+4,SLIDER_BASE+17), BPoint(fPositionX+4,SLIDER_BASE+16), black);
|
||||
AddLine(BPoint(fPositionX+4,SLIDER_BASE+16), BPoint(fPositionX,SLIDER_BASE+12), black);
|
||||
AddLine(BPoint(fPositionX-4,SLIDER_BASE+18), BPoint(fPositionX+4,SLIDER_BASE+18), white);
|
||||
|
||||
AddLine(BPoint(fPositionX-3,SLIDER_BASE+16), BPoint(fPositionX,SLIDER_BASE+13), rose);
|
||||
|
||||
AddLine(BPoint(fPositionX-2,SLIDER_BASE+16), BPoint(fPositionX+2,SLIDER_BASE+16), red);
|
||||
AddLine(BPoint(fPositionX-1,SLIDER_BASE+15), BPoint(fPositionX+1,SLIDER_BASE+15), red);
|
||||
AddLine(BPoint(fPositionX,SLIDER_BASE+14), BPoint(fPositionX,SLIDER_BASE+14), red);
|
||||
|
||||
AddLine(BPoint(fPositionX+1,SLIDER_BASE+14), BPoint(fPositionX+3,SLIDER_BASE+16), bordeau);
|
||||
|
||||
EndLineArray();
|
||||
|
||||
|
||||
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::DrawCounter(bigtime_t timestamp, float position, bool isTracking)
|
||||
{
|
||||
// timecounter
|
||||
|
||||
rgb_color gray = {128,128,128};
|
||||
rgb_color blue = {0,0,140};
|
||||
rgb_color blue2 = {146,146,214};
|
||||
rgb_color white = {255,255,255};
|
||||
|
||||
float counterX = position;
|
||||
if (counterX < 39)
|
||||
counterX = 39;
|
||||
if (counterX > fRight - 23)
|
||||
counterX = fRight - 23;
|
||||
BeginLineArray(30);
|
||||
if (!isTracking) {
|
||||
AddLine(BPoint(counterX-24,SLIDER_BASE+1), BPoint(counterX+24,SLIDER_BASE+1), gray);
|
||||
AddLine(BPoint(counterX+25,SLIDER_BASE+1), BPoint(counterX+25,SLIDER_BASE-8), gray);
|
||||
AddLine(BPoint(counterX-25,SLIDER_BASE+1), BPoint(counterX-25,SLIDER_BASE-9), white);
|
||||
AddLine(BPoint(counterX-24,SLIDER_BASE-9), BPoint(counterX+25,SLIDER_BASE-9), white);
|
||||
SetHighColor(216,216,216);
|
||||
} else {
|
||||
AddLine(BPoint(counterX-24,SLIDER_BASE+1), BPoint(counterX+24,SLIDER_BASE+1), blue);
|
||||
AddLine(BPoint(counterX+25,SLIDER_BASE+1), BPoint(counterX+25,SLIDER_BASE-9), blue2);
|
||||
AddLine(BPoint(counterX-25,SLIDER_BASE+1), BPoint(counterX-25,SLIDER_BASE-9), blue2);
|
||||
AddLine(BPoint(counterX-24,SLIDER_BASE-9), BPoint(counterX+24,SLIDER_BASE-9), blue2);
|
||||
SetHighColor(48,48,241);
|
||||
}
|
||||
EndLineArray();
|
||||
FillRect(BRect(counterX-24,SLIDER_BASE-8,counterX+24,SLIDER_BASE));
|
||||
|
||||
SetDrawingMode(B_OP_COPY);
|
||||
if (isTracking)
|
||||
SetHighColor(255,255,255);
|
||||
else
|
||||
SetHighColor(0,0,0);
|
||||
SetLowColor(ViewColor());
|
||||
|
||||
SetFont(&fFont);
|
||||
char string[12];
|
||||
TimeToString(timestamp, string);
|
||||
DrawString(string, BPoint(counterX-22, SLIDER_BASE-1));
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
|
||||
{
|
||||
if (!IsTracking())
|
||||
return;
|
||||
|
||||
uint32 mouseButtons;
|
||||
BPoint where;
|
||||
GetMouse(&where, &mouseButtons, true);
|
||||
|
||||
// button not pressed, exit
|
||||
if (! (mouseButtons & B_PRIMARY_MOUSE_BUTTON)) {
|
||||
Invoke();
|
||||
SetTracking(false);
|
||||
}
|
||||
|
||||
UpdatePosition(point);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::MouseDown(BPoint point)
|
||||
{
|
||||
if (!Bounds().InsetBySelf(2,2).Contains(point))
|
||||
return;
|
||||
|
||||
UpdatePosition(point);
|
||||
SetTracking(true);
|
||||
SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::MouseUp(BPoint point)
|
||||
{
|
||||
if (!IsTracking())
|
||||
return;
|
||||
if (Bounds().InsetBySelf(2,2).Contains(point)) {
|
||||
UpdatePosition(point);
|
||||
}
|
||||
|
||||
fLeftTracking = fRightTracking = fMainTracking = false;
|
||||
|
||||
Invoke();
|
||||
SetTracking(false);
|
||||
Draw(Bounds());
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::UpdatePosition(BPoint point)
|
||||
{
|
||||
BRect leftRect(fLeftX-9, SLIDER_BASE+3, fLeftX, SLIDER_BASE+16);
|
||||
BRect rightRect(fRightX, SLIDER_BASE+3, fRightX+9, SLIDER_BASE+16);
|
||||
|
||||
if (!(fRightTracking || fMainTracking) && (fLeftTracking || ((point.x < fPositionX-4) && leftRect.Contains(point)))) {
|
||||
if (!IsTracking())
|
||||
fLastX = point.x - fLeftX;
|
||||
fLeftX = MIN(MAX(point.x - fLastX, 15), fRight);
|
||||
fLeftTime = (bigtime_t)(MAX(MIN((fLeftX - 15) / (fRight - 14),1), 0) * fTotalTime);
|
||||
fLeftTracking = true;
|
||||
|
||||
BMessage msg = *Message();
|
||||
msg.AddInt64("left", fLeftTime);
|
||||
|
||||
if (fPositionX < fLeftX) {
|
||||
fPositionX = fLeftX + 1;
|
||||
fMainTime = fLeftTime;
|
||||
msg.AddInt64("main", fMainTime);
|
||||
if (fRightX < fPositionX) {
|
||||
fRightX = fPositionX;
|
||||
fRightTime = fMainTime;
|
||||
msg.AddInt64("right", fRightTime);
|
||||
}
|
||||
}
|
||||
|
||||
Invoke(&msg);
|
||||
|
||||
//printf("fLeftPos : %Ld\n", fLeftTime);
|
||||
} else if (!fMainTracking && (fRightTracking || ((point.x > fPositionX+4) && rightRect.Contains(point)))) {
|
||||
if (!IsTracking())
|
||||
fLastX = point.x - fRightX;
|
||||
fRightX = MIN(MAX(point.x - fLastX, 15), fRight);
|
||||
fRightTime = (bigtime_t)(MAX(MIN((fRightX - 15) / (fRight - 14),1), 0) * fTotalTime);
|
||||
fRightTracking = true;
|
||||
|
||||
BMessage msg = *Message();
|
||||
msg.AddInt64("right", fRightTime);
|
||||
|
||||
if (fPositionX > fRightX) {
|
||||
fPositionX = fRightX;
|
||||
fMainTime = fRightTime;
|
||||
msg.AddInt64("main", fMainTime);
|
||||
if (fLeftX > fPositionX) {
|
||||
fLeftX = fPositionX - 1;
|
||||
fLeftTime = fMainTime;
|
||||
msg.AddInt64("left", fLeftTime);
|
||||
}
|
||||
}
|
||||
|
||||
Invoke(&msg);
|
||||
|
||||
//printf("fRightPos : %Ld\n", fRightTime);
|
||||
} else {
|
||||
fPositionX = MIN(MAX(point.x, 15), fRight);
|
||||
fMainTime = (bigtime_t)(MAX(MIN((fPositionX - 15) / (fRight - 14),1), 0) * fTotalTime);
|
||||
fMainTracking = true;
|
||||
|
||||
BMessage msg = *Message();
|
||||
msg.AddInt64("main", fMainTime);
|
||||
|
||||
if (fRightX < fPositionX) {
|
||||
fRightX = fPositionX;
|
||||
fRightTime = fMainTime;
|
||||
msg.AddInt64("right", fRightTime);
|
||||
} else if (fLeftX > fPositionX) {
|
||||
fLeftX = fPositionX - 1;
|
||||
fLeftTime = fMainTime;
|
||||
msg.AddInt64("left", fLeftTime);
|
||||
}
|
||||
|
||||
Invoke(&msg);
|
||||
//printf("fPosition : %Ld\n", fMainTime);
|
||||
}
|
||||
Draw(Bounds());
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::TimeToString(bigtime_t timestamp, char *string)
|
||||
{
|
||||
uint32 hours = timestamp / 3600000000LL;
|
||||
timestamp -= hours * 3600000000LL;
|
||||
uint32 minutes = timestamp / 60000000LL;
|
||||
timestamp -= minutes * 60000000LL;
|
||||
uint32 seconds = timestamp / 1000000LL;
|
||||
timestamp -= seconds * 1000000LL;
|
||||
uint32 centiseconds = timestamp / 10000LL;
|
||||
sprintf(string, "%02ld:%02ld:%02ld:%02ld", hours, minutes, seconds, centiseconds);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::SetMainTime(bigtime_t timestamp, bool reset)
|
||||
{
|
||||
fMainTime = timestamp;
|
||||
fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime);
|
||||
if (reset) {
|
||||
fRightTime = fTotalTime;
|
||||
fLeftTime = 0;
|
||||
fLeftX = 14 + (fRight - 15) * ((double)fLeftTime / fTotalTime);
|
||||
fRightX = 15 + (fRight - 16) * ((double)fRightTime / fTotalTime);
|
||||
}
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void
|
||||
TrackSlider::SetTotalTime(bigtime_t timestamp, bool reset)
|
||||
{
|
||||
fTotalTime = timestamp;
|
||||
if (reset) {
|
||||
fMainTime = 0;
|
||||
fRightTime = fTotalTime;
|
||||
fLeftTime = 0;
|
||||
}
|
||||
fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime);
|
||||
fLeftX = 14 + (fRight - 15) * ((double)fLeftTime / fTotalTime);
|
||||
fRightX = 15 + (fRight - 16) * ((double)fRightTime / fTotalTime);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::ResetMainTime()
|
||||
{
|
||||
fMainTime = fLeftTime;
|
||||
fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackSlider::FrameResized(float width, float height)
|
||||
{
|
||||
fRight = Bounds().right - kLeftRightTrackSliderWidth;
|
||||
fPositionX = 15 + (fRight - 14) * ((double)fMainTime / fTotalTime);
|
||||
fLeftX = 14 + (fRight - 15) * ((double)fLeftTime / fTotalTime);
|
||||
fRightX = 15 + (fRight - 16) * ((double)fRightTime / fTotalTime);
|
||||
Invalidate();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
#ifndef TRACKSLIDER_H
|
||||
#define TRACKSLIDER_H
|
||||
|
||||
#include <Control.h>
|
||||
#include <Bitmap.h>
|
||||
#include <Font.h>
|
||||
|
||||
class TrackSlider : public BControl
|
||||
{
|
||||
public:
|
||||
TrackSlider(BRect rect, const char* title, BMessage *msg, uint32 resizeFlags);
|
||||
~TrackSlider();
|
||||
void AttachedToWindow();
|
||||
virtual void Draw(BRect);
|
||||
virtual void MouseMoved(BPoint point, uint32 transit, const BMessage *message);
|
||||
virtual void MouseUp(BPoint point);
|
||||
virtual void MouseDown(BPoint point);
|
||||
void SetMainTime(bigtime_t timestamp, bool reset);
|
||||
void SetTotalTime(bigtime_t timestamp, bool reset);
|
||||
bigtime_t * MainTime() { return &fMainTime; };
|
||||
bigtime_t RightTime() { return fRightTime; };
|
||||
bigtime_t LeftTime() { return fLeftTime; };
|
||||
void ResetMainTime();
|
||||
virtual void FrameResized(float width, float height);
|
||||
private:
|
||||
void DrawCounter(bigtime_t timestamp, float position, bool isTracking);
|
||||
void TimeToString(bigtime_t timestamp, char *string);
|
||||
void UpdatePosition(BPoint point);
|
||||
BBitmap leftBitmap, rightBitmap, leftThumbBitmap, rightThumbBitmap;
|
||||
float fRight;
|
||||
|
||||
bigtime_t fLeftTime;
|
||||
bigtime_t fRightTime;
|
||||
bigtime_t fMainTime;
|
||||
bigtime_t fTotalTime;
|
||||
|
||||
float fPositionX;
|
||||
float fLeftX;
|
||||
float fRightX;
|
||||
float fLastX;
|
||||
bool fLeftTracking;
|
||||
bool fRightTracking;
|
||||
bool fMainTracking;
|
||||
|
||||
BFont fFont;
|
||||
};
|
||||
|
||||
#endif /* TRACKSLIDER_H */
|
||||
@@ -0,0 +1,734 @@
|
||||
/*
|
||||
* 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 <Bitmap.h>
|
||||
#include <Debug.h>
|
||||
#include <MessageFilter.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "TransportButton.h"
|
||||
#include "DrawingTidbits.h"
|
||||
|
||||
class BitmapStash {
|
||||
// Bitmap stash is a simple class to hold all the lazily-allocated
|
||||
// bitmaps that the TransportButton needs when rendering itself.
|
||||
// signature is a combination of the different enabled, pressed, playing, etc.
|
||||
// flavors of a bitmap. If the stash does not have a particular bitmap,
|
||||
// it turns around to ask the button to create one and stores it for next time.
|
||||
public:
|
||||
BitmapStash(TransportButton *);
|
||||
~BitmapStash();
|
||||
BBitmap *GetBitmap(uint32 signature);
|
||||
|
||||
private:
|
||||
TransportButton *owner;
|
||||
map<uint32, BBitmap *> stash;
|
||||
};
|
||||
|
||||
|
||||
BitmapStash::BitmapStash(TransportButton *owner)
|
||||
: owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BBitmap *
|
||||
BitmapStash::GetBitmap(uint32 signature)
|
||||
{
|
||||
if (stash.find(signature) == stash.end()) {
|
||||
BBitmap *newBits = owner->MakeBitmap(signature);
|
||||
ASSERT(newBits);
|
||||
stash[signature] = newBits;
|
||||
}
|
||||
|
||||
return stash[signature];
|
||||
}
|
||||
|
||||
|
||||
BitmapStash::~BitmapStash()
|
||||
{
|
||||
// delete all the bitmaps
|
||||
for (map<uint32, BBitmap *>::iterator i = stash.begin(); i != stash.end(); i++)
|
||||
delete (*i).second;
|
||||
}
|
||||
|
||||
|
||||
class PeriodicMessageSender {
|
||||
// used to send a specified message repeatedly when holding down a button
|
||||
public:
|
||||
static PeriodicMessageSender *Launch(BMessenger target,
|
||||
const BMessage *message, bigtime_t period);
|
||||
void Quit();
|
||||
|
||||
private:
|
||||
PeriodicMessageSender(BMessenger target, const BMessage *message,
|
||||
bigtime_t period);
|
||||
~PeriodicMessageSender() {}
|
||||
// use quit
|
||||
|
||||
static status_t TrackBinder(void *);
|
||||
void Run();
|
||||
|
||||
BMessenger target;
|
||||
BMessage message;
|
||||
|
||||
bigtime_t period;
|
||||
|
||||
bool requestToQuit;
|
||||
};
|
||||
|
||||
|
||||
PeriodicMessageSender::PeriodicMessageSender(BMessenger target,
|
||||
const BMessage *message, bigtime_t period)
|
||||
: target(target),
|
||||
message(*message),
|
||||
period(period),
|
||||
requestToQuit(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
PeriodicMessageSender *
|
||||
PeriodicMessageSender::Launch(BMessenger target, const BMessage *message,
|
||||
bigtime_t period)
|
||||
{
|
||||
PeriodicMessageSender *result = new PeriodicMessageSender(target, message, period);
|
||||
thread_id thread = spawn_thread(&PeriodicMessageSender::TrackBinder,
|
||||
"ButtonRepeatingThread", B_NORMAL_PRIORITY, result);
|
||||
|
||||
if (thread <= 0 || resume_thread(thread) != B_OK) {
|
||||
// didn't start, don't leak self
|
||||
delete result;
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PeriodicMessageSender::Quit()
|
||||
{
|
||||
requestToQuit = true;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
PeriodicMessageSender::TrackBinder(void *castToThis)
|
||||
{
|
||||
((PeriodicMessageSender *)castToThis)->Run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PeriodicMessageSender::Run()
|
||||
{
|
||||
for (;;) {
|
||||
snooze(period);
|
||||
if (requestToQuit)
|
||||
break;
|
||||
target.SendMessage(&message);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
class SkipButtonKeypressFilter : public BMessageFilter {
|
||||
public:
|
||||
SkipButtonKeypressFilter(uint32 shortcutKey, uint32 shortcutModifier,
|
||||
TransportButton *target);
|
||||
|
||||
protected:
|
||||
filter_result Filter(BMessage *message, BHandler **handler);
|
||||
|
||||
private:
|
||||
uint32 shortcutKey;
|
||||
uint32 shortcutModifier;
|
||||
TransportButton *target;
|
||||
};
|
||||
|
||||
|
||||
SkipButtonKeypressFilter::SkipButtonKeypressFilter(uint32 shortcutKey,
|
||||
uint32 shortcutModifier, TransportButton *target)
|
||||
: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
|
||||
shortcutKey(shortcutKey),
|
||||
shortcutModifier(shortcutModifier),
|
||||
target(target)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
filter_result
|
||||
SkipButtonKeypressFilter::Filter(BMessage *message, BHandler **handler)
|
||||
{
|
||||
if (target->IsEnabled()
|
||||
&& (message->what == B_KEY_DOWN || message->what == B_KEY_UP)) {
|
||||
uint32 modifiers;
|
||||
uint32 rawKeyChar = 0;
|
||||
uint8 byte = 0;
|
||||
int32 key = 0;
|
||||
|
||||
if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
|
||||
|| message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
|
||||
|| message->FindInt8("byte", (int8 *)&byte) != B_OK
|
||||
|| message->FindInt32("key", &key) != B_OK)
|
||||
return B_DISPATCH_MESSAGE;
|
||||
|
||||
modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
|
||||
| B_OPTION_KEY | B_MENU_KEY;
|
||||
// strip caps lock, etc.
|
||||
|
||||
if (modifiers == shortcutModifier && rawKeyChar == shortcutKey) {
|
||||
if (message->what == B_KEY_DOWN)
|
||||
target->ShortcutKeyDown();
|
||||
else
|
||||
target->ShortcutKeyUp();
|
||||
|
||||
return B_SKIP_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
// let others deal with this
|
||||
return B_DISPATCH_MESSAGE;
|
||||
}
|
||||
|
||||
|
||||
TransportButton::TransportButton(BRect frame, const char *name,
|
||||
const unsigned char *normalBits,
|
||||
const unsigned char *pressedBits,
|
||||
const unsigned char *disabledBits,
|
||||
BMessage *invokeMessage, BMessage *startPressingMessage,
|
||||
BMessage *pressingMessage, BMessage *donePressingMessage, bigtime_t period,
|
||||
uint32 key, uint32 modifiers, uint32 resizeFlags)
|
||||
: BControl(frame, name, "", invokeMessage, resizeFlags, B_WILL_DRAW | B_NAVIGABLE),
|
||||
bitmaps(new BitmapStash(this)),
|
||||
normalBits(normalBits),
|
||||
pressedBits(pressedBits),
|
||||
disabledBits(disabledBits),
|
||||
startPressingMessage(startPressingMessage),
|
||||
pressingMessage(pressingMessage),
|
||||
donePressingMessage(donePressingMessage),
|
||||
pressingPeriod(period),
|
||||
mouseDown(false),
|
||||
keyDown(false),
|
||||
messageSender(0),
|
||||
keyPressFilter(0)
|
||||
{
|
||||
if (key)
|
||||
keyPressFilter = new SkipButtonKeypressFilter(key, modifiers, this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::AttachedToWindow()
|
||||
{
|
||||
_inherited::AttachedToWindow();
|
||||
if (keyPressFilter)
|
||||
Window()->AddCommonFilter(keyPressFilter);
|
||||
|
||||
// transparent to reduce flicker
|
||||
SetViewColor(B_TRANSPARENT_COLOR);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::DetachedFromWindow()
|
||||
{
|
||||
if (keyPressFilter) {
|
||||
Window()->RemoveCommonFilter(keyPressFilter);
|
||||
delete keyPressFilter;
|
||||
}
|
||||
_inherited::DetachedFromWindow();
|
||||
}
|
||||
|
||||
|
||||
TransportButton::~TransportButton()
|
||||
{
|
||||
delete startPressingMessage;
|
||||
delete pressingMessage;
|
||||
delete donePressingMessage;
|
||||
delete bitmaps;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::WindowActivated(bool state)
|
||||
{
|
||||
if (!state)
|
||||
ShortcutKeyUp();
|
||||
|
||||
_inherited::WindowActivated(state);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::SetEnabled(bool on)
|
||||
{
|
||||
_inherited::SetEnabled(on);
|
||||
if (!on)
|
||||
ShortcutKeyUp();
|
||||
}
|
||||
|
||||
|
||||
const unsigned char *
|
||||
TransportButton::BitsForMask(uint32 mask) const
|
||||
{
|
||||
switch (mask) {
|
||||
case 0:
|
||||
return normalBits;
|
||||
case kDisabledMask:
|
||||
return disabledBits;
|
||||
case kPressedMask:
|
||||
return pressedBits;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
TRESPASS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
BBitmap *
|
||||
TransportButton::MakeBitmap(uint32 mask)
|
||||
{
|
||||
BBitmap *result = new BBitmap(Bounds(), B_COLOR_8_BIT);
|
||||
result->SetBits(BitsForMask(mask), (Bounds().Width() + 1) * (Bounds().Height() + 1),
|
||||
0, B_COLOR_8_BIT);
|
||||
|
||||
ReplaceTransparentColor(result, Parent()->ViewColor());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
TransportButton::ModeMask() const
|
||||
{
|
||||
return (IsEnabled() ? 0 : kDisabledMask)
|
||||
| (Value() ? kPressedMask : 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::Draw(BRect)
|
||||
{
|
||||
DrawBitmapAsync(bitmaps->GetBitmap(ModeMask()));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::StartPressing()
|
||||
{
|
||||
SetValue(1);
|
||||
if (startPressingMessage)
|
||||
Invoke(startPressingMessage);
|
||||
|
||||
if (pressingMessage) {
|
||||
ASSERT(pressingMessage);
|
||||
messageSender = PeriodicMessageSender::Launch(Messenger(),
|
||||
pressingMessage, pressingPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::MouseCancelPressing()
|
||||
{
|
||||
if (!mouseDown || keyDown)
|
||||
return;
|
||||
|
||||
mouseDown = false;
|
||||
|
||||
if (pressingMessage) {
|
||||
ASSERT(messageSender);
|
||||
PeriodicMessageSender *sender = messageSender;
|
||||
messageSender = 0;
|
||||
sender->Quit();
|
||||
}
|
||||
|
||||
if (donePressingMessage)
|
||||
Invoke(donePressingMessage);
|
||||
SetValue(0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::DonePressing()
|
||||
{
|
||||
if (pressingMessage) {
|
||||
ASSERT(messageSender);
|
||||
PeriodicMessageSender *sender = messageSender;
|
||||
messageSender = 0;
|
||||
sender->Quit();
|
||||
}
|
||||
|
||||
Invoke();
|
||||
SetValue(0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::MouseStartPressing()
|
||||
{
|
||||
if (mouseDown)
|
||||
return;
|
||||
|
||||
mouseDown = true;
|
||||
if (!keyDown)
|
||||
StartPressing();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::MouseDonePressing()
|
||||
{
|
||||
if (!mouseDown)
|
||||
return;
|
||||
|
||||
mouseDown = false;
|
||||
if (!keyDown)
|
||||
DonePressing();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::ShortcutKeyDown()
|
||||
{
|
||||
if (!IsEnabled())
|
||||
return;
|
||||
|
||||
if (keyDown)
|
||||
return;
|
||||
|
||||
keyDown = true;
|
||||
if (!mouseDown)
|
||||
StartPressing();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::ShortcutKeyUp()
|
||||
{
|
||||
if (!keyDown)
|
||||
return;
|
||||
|
||||
keyDown = false;
|
||||
if (!mouseDown)
|
||||
DonePressing();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TransportButton::MouseDown(BPoint)
|
||||
{
|
||||
if (!IsEnabled())
|
||||
return;
|
||||
|
||||
ASSERT(Window()->Flags() & B_ASYNCHRONOUS_CONTROLS);
|
||||
SetTracking(true);
|
||||
SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
|
||||
MouseStartPressing();
|
||||
}
|
||||
|
||||
void
|
||||
TransportButton::MouseMoved(BPoint point, uint32 code, const BMessage *)
|
||||
{
|
||||
if (IsTracking() && Bounds().Contains(point) != Value()) {
|
||||
if (!Value())
|
||||
MouseStartPressing();
|
||||
else
|
||||
MouseCancelPressing();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TransportButton::MouseUp(BPoint point)
|
||||
{
|
||||
if (IsTracking()) {
|
||||
if (Bounds().Contains(point))
|
||||
MouseDonePressing();
|
||||
else
|
||||
MouseCancelPressing();
|
||||
SetTracking(false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TransportButton::SetStartPressingMessage(BMessage *message)
|
||||
{
|
||||
delete startPressingMessage;
|
||||
startPressingMessage = message;
|
||||
}
|
||||
|
||||
void
|
||||
TransportButton::SetPressingMessage(BMessage *message)
|
||||
{
|
||||
delete pressingMessage;
|
||||
pressingMessage = message;
|
||||
}
|
||||
|
||||
void
|
||||
TransportButton::SetDonePressingMessage(BMessage *message)
|
||||
{
|
||||
delete donePressingMessage;
|
||||
donePressingMessage = message;
|
||||
}
|
||||
|
||||
void
|
||||
TransportButton::SetPressingPeriod(bigtime_t newTime)
|
||||
{
|
||||
pressingPeriod = newTime;
|
||||
}
|
||||
|
||||
|
||||
PlayPauseButton::PlayPauseButton(BRect frame, const char *name,
|
||||
BMessage *invokeMessage, BMessage *blinkMessage,
|
||||
uint32 key, uint32 modifiers, uint32 resizeFlags)
|
||||
: TransportButton(frame, name, kPlayButtonBitmapBits, kPressedPlayButtonBitmapBits,
|
||||
kDisabledPlayButtonBitmapBits, invokeMessage, NULL,
|
||||
NULL, NULL, 0, key, modifiers, resizeFlags),
|
||||
fState(PlayPauseButton::kStopped),
|
||||
fLastModeMask(0),
|
||||
fRunner(NULL),
|
||||
fBlinkMessage(blinkMessage)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
PlayPauseButton::SetStopped()
|
||||
{
|
||||
if (fState == kStopped || fState == kAboutToPlay)
|
||||
return;
|
||||
|
||||
fState = kStopped;
|
||||
delete fRunner;
|
||||
fRunner = NULL;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void
|
||||
PlayPauseButton::SetPlaying()
|
||||
{
|
||||
if (fState == kAboutToPause)
|
||||
return;
|
||||
|
||||
// in playing state blink the LED on and off
|
||||
if (fState == kPlayingLedOn)
|
||||
fState = kPlayingLedOff;
|
||||
else
|
||||
fState = kPlayingLedOn;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
const bigtime_t kPlayingBlinkPeriod = 600000;
|
||||
|
||||
void
|
||||
PlayPauseButton::SetPaused()
|
||||
{
|
||||
if (fState == kAboutToPlay)
|
||||
return;
|
||||
|
||||
// in paused state blink the LED on and off
|
||||
if (fState == kPausedLedOn)
|
||||
fState = kPausedLedOff;
|
||||
else
|
||||
fState = kPausedLedOn;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
uint32
|
||||
PlayPauseButton::ModeMask() const
|
||||
{
|
||||
if (!IsEnabled())
|
||||
return kDisabledMask;
|
||||
|
||||
uint32 result = 0;
|
||||
|
||||
if (Value())
|
||||
result = kPressedMask;
|
||||
|
||||
if (fState == kPlayingLedOn || fState == kAboutToPlay)
|
||||
result |= kPlayingMask;
|
||||
else if (fState == kAboutToPause || fState == kPausedLedOn)
|
||||
result |= kPausedMask;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const unsigned char *
|
||||
PlayPauseButton::BitsForMask(uint32 mask) const
|
||||
{
|
||||
switch (mask) {
|
||||
case kPlayingMask:
|
||||
return kPlayingPlayButtonBitmapBits;
|
||||
case kPlayingMask | kPressedMask:
|
||||
return kPressedPlayingPlayButtonBitmapBits;
|
||||
case kPausedMask:
|
||||
return kPausedPlayButtonBitmapBits;
|
||||
case kPausedMask | kPressedMask:
|
||||
return kPressedPausedPlayButtonBitmapBits;
|
||||
default:
|
||||
return _inherited::BitsForMask(mask);
|
||||
}
|
||||
TRESPASS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlayPauseButton::StartPressing()
|
||||
{
|
||||
if (fState == kPlayingLedOn || fState == kPlayingLedOff)
|
||||
fState = kAboutToPause;
|
||||
else
|
||||
fState = kAboutToPlay;
|
||||
|
||||
_inherited::StartPressing();
|
||||
}
|
||||
|
||||
void
|
||||
PlayPauseButton::MouseCancelPressing()
|
||||
{
|
||||
if (fState == kAboutToPause)
|
||||
fState = kPlayingLedOn;
|
||||
else
|
||||
fState = kStopped;
|
||||
|
||||
_inherited::MouseCancelPressing();
|
||||
}
|
||||
|
||||
void
|
||||
PlayPauseButton::DonePressing()
|
||||
{
|
||||
if (fState == kAboutToPause) {
|
||||
fState = kPausedLedOn;
|
||||
} else if (fState == kAboutToPlay) {
|
||||
fState = kPlayingLedOn;
|
||||
if (!fRunner && fBlinkMessage)
|
||||
fRunner = new BMessageRunner(Messenger(), fBlinkMessage, kPlayingBlinkPeriod);
|
||||
}
|
||||
|
||||
_inherited::DonePressing();
|
||||
}
|
||||
|
||||
|
||||
RecordButton::RecordButton(BRect frame, const char *name,
|
||||
BMessage *invokeMessage, BMessage *blinkMessage,
|
||||
uint32 key, uint32 modifiers, uint32 resizeFlags)
|
||||
: TransportButton(frame, name, kRecordButtonBitmapBits, kPressedRecordButtonBitmapBits,
|
||||
kDisabledRecordButtonBitmapBits, invokeMessage, NULL, NULL,
|
||||
NULL, 0, key, modifiers, resizeFlags),
|
||||
fState(RecordButton::kStopped),
|
||||
fLastModeMask(0),
|
||||
fRunner(NULL),
|
||||
fBlinkMessage(blinkMessage)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
RecordButton::SetStopped()
|
||||
{
|
||||
if (fState == kStopped || fState == kAboutToRecord)
|
||||
return;
|
||||
|
||||
fState = kStopped;
|
||||
delete fRunner;
|
||||
fRunner = NULL;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
const bigtime_t kRecordingBlinkPeriod = 600000;
|
||||
|
||||
void
|
||||
RecordButton::SetRecording()
|
||||
{
|
||||
if (fState == kAboutToStop)
|
||||
return;
|
||||
|
||||
if (fState == kRecordingLedOff)
|
||||
fState = kRecordingLedOn;
|
||||
else
|
||||
fState = kRecordingLedOff;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
uint32
|
||||
RecordButton::ModeMask() const
|
||||
{
|
||||
if (!IsEnabled())
|
||||
return kDisabledMask;
|
||||
|
||||
uint32 result = 0;
|
||||
|
||||
if (Value())
|
||||
result = kPressedMask;
|
||||
|
||||
if (fState == kAboutToStop || fState == kRecordingLedOn)
|
||||
result |= kRecordingMask;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const unsigned char *
|
||||
RecordButton::BitsForMask(uint32 mask) const
|
||||
{
|
||||
switch (mask) {
|
||||
case kRecordingMask:
|
||||
return kRecordingRecordButtonBitmapBits;
|
||||
case kRecordingMask | kPressedMask:
|
||||
return kPressedRecordingRecordButtonBitmapBits;
|
||||
default:
|
||||
return _inherited::BitsForMask(mask);
|
||||
}
|
||||
TRESPASS();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RecordButton::StartPressing()
|
||||
{
|
||||
if (fState == kRecordingLedOn || fState == kRecordingLedOff)
|
||||
fState = kAboutToStop;
|
||||
else
|
||||
fState = kAboutToRecord;
|
||||
|
||||
_inherited::StartPressing();
|
||||
}
|
||||
|
||||
void
|
||||
RecordButton::MouseCancelPressing()
|
||||
{
|
||||
if (fState == kAboutToStop)
|
||||
fState = kRecordingLedOn;
|
||||
else
|
||||
fState = kStopped;
|
||||
|
||||
_inherited::MouseCancelPressing();
|
||||
}
|
||||
|
||||
void
|
||||
RecordButton::DonePressing()
|
||||
{
|
||||
if (fState == kAboutToStop) {
|
||||
fState = kStopped;
|
||||
delete fRunner;
|
||||
fRunner = NULL;
|
||||
} else if (fState == kAboutToRecord) {
|
||||
fState = kRecordingLedOn;
|
||||
if (!fRunner && fBlinkMessage)
|
||||
fRunner = new BMessageRunner(Messenger(), fBlinkMessage, kRecordingBlinkPeriod);
|
||||
}
|
||||
|
||||
_inherited::DonePressing();
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#ifndef __MEDIA_BUTTON__
|
||||
#define __MEDIA_BUTTON__
|
||||
|
||||
#include <Control.h>
|
||||
#include <MessageRunner.h>
|
||||
#include "icon_button.h"
|
||||
|
||||
class BMessage;
|
||||
class BBitmap;
|
||||
class PeriodicMessageSender;
|
||||
class BitmapStash;
|
||||
|
||||
// TransportButton must be installed into a window with B_ASYNCHRONOUS_CONTROLS on
|
||||
// currently no button focus drawing
|
||||
|
||||
class TransportButton : public BControl {
|
||||
public:
|
||||
|
||||
TransportButton(BRect frame, const char *name,
|
||||
const unsigned char *normalBits,
|
||||
const unsigned char *pressedBits,
|
||||
const unsigned char *disabledBits,
|
||||
BMessage *invokeMessage, // done pressing over button
|
||||
BMessage *startPressingMessage = 0, // just clicked button
|
||||
BMessage *pressingMessage = 0, // periodical still pressing
|
||||
BMessage *donePressing = 0, // tracked out of button/didn't invoke
|
||||
bigtime_t period = 0, // pressing message period
|
||||
uint32 key = 0, // optional shortcut key
|
||||
uint32 modifiers = 0, // optional shortcut key modifier
|
||||
uint32 resizeFlags = B_FOLLOW_LEFT | B_FOLLOW_TOP);
|
||||
|
||||
virtual ~TransportButton();
|
||||
|
||||
void SetStartPressingMessage(BMessage *);
|
||||
void SetPressingMessage(BMessage *);
|
||||
void SetDonePressingMessage(BMessage *);
|
||||
void SetPressingPeriod(bigtime_t);
|
||||
|
||||
virtual void SetEnabled(bool);
|
||||
|
||||
protected:
|
||||
|
||||
enum {
|
||||
kDisabledMask = 0x1,
|
||||
kPressedMask = 0x2
|
||||
};
|
||||
|
||||
virtual void AttachedToWindow();
|
||||
virtual void DetachedFromWindow();
|
||||
virtual void Draw(BRect);
|
||||
virtual void MouseDown(BPoint);
|
||||
virtual void MouseMoved(BPoint, uint32 code, const BMessage *);
|
||||
virtual void MouseUp(BPoint);
|
||||
virtual void WindowActivated(bool);
|
||||
|
||||
virtual BBitmap *MakeBitmap(uint32);
|
||||
// lazy bitmap builder
|
||||
|
||||
virtual uint32 ModeMask() const;
|
||||
// mode mask corresponding to the current button state
|
||||
// - determines which bitmap will be used
|
||||
virtual const unsigned char *BitsForMask(uint32) const;
|
||||
// pick the right bits based on a mode mask
|
||||
|
||||
// overriding class can add swapping between two pairs of bitmaps, etc.
|
||||
virtual void StartPressing();
|
||||
virtual void MouseCancelPressing();
|
||||
virtual void DonePressing();
|
||||
|
||||
private:
|
||||
void ShortcutKeyDown();
|
||||
void ShortcutKeyUp();
|
||||
|
||||
void MouseStartPressing();
|
||||
void MouseDonePressing();
|
||||
|
||||
BitmapStash *bitmaps;
|
||||
// using BitmapStash * here instead of a direct member so that the class can be private in
|
||||
// the .cpp file
|
||||
|
||||
// bitmap bits used to build bitmaps for the different states
|
||||
const unsigned char *normalBits;
|
||||
const unsigned char *pressedBits;
|
||||
const unsigned char *disabledBits;
|
||||
|
||||
BMessage *startPressingMessage;
|
||||
BMessage *pressingMessage;
|
||||
BMessage *donePressingMessage;
|
||||
bigtime_t pressingPeriod;
|
||||
|
||||
bool mouseDown;
|
||||
bool keyDown;
|
||||
PeriodicMessageSender *messageSender;
|
||||
BMessageFilter *keyPressFilter;
|
||||
|
||||
typedef BControl _inherited;
|
||||
|
||||
friend class SkipButtonKeypressFilter;
|
||||
friend class BitmapStash;
|
||||
};
|
||||
|
||||
class PlayPauseButton : public TransportButton {
|
||||
// Knows about playing and paused states, blinks
|
||||
// the pause LED during paused state
|
||||
public:
|
||||
PlayPauseButton(BRect frame, const char *name,
|
||||
BMessage *invokeMessage, // done pressing over button
|
||||
BMessage *blinkMessage = 0, // blinking
|
||||
uint32 key = 0, // optional shortcut key
|
||||
uint32 modifiers = 0, // optional shortcut key modifier
|
||||
uint32 resizeFlags = B_FOLLOW_LEFT | B_FOLLOW_TOP);
|
||||
|
||||
// These need get called periodically to update the button state
|
||||
// OK to call them over and over - once the state is correct, the call
|
||||
// is very low overhead
|
||||
void SetStopped();
|
||||
void SetPlaying();
|
||||
void SetPaused();
|
||||
|
||||
protected:
|
||||
|
||||
virtual uint32 ModeMask() const;
|
||||
virtual const unsigned char *BitsForMask(uint32) const;
|
||||
|
||||
virtual void StartPressing();
|
||||
virtual void MouseCancelPressing();
|
||||
virtual void DonePressing();
|
||||
|
||||
private:
|
||||
enum PlayState {
|
||||
kStopped,
|
||||
kAboutToPlay,
|
||||
kPlayingLedOn,
|
||||
kPlayingLedOff,
|
||||
kAboutToPause,
|
||||
kPausedLedOn,
|
||||
kPausedLedOff
|
||||
};
|
||||
|
||||
enum {
|
||||
kPlayingMask = 0x4,
|
||||
kPausedMask = 0x8
|
||||
};
|
||||
|
||||
PlayState fState;
|
||||
uint32 fLastModeMask;
|
||||
BMessageRunner *fRunner;
|
||||
BMessage *fBlinkMessage;
|
||||
|
||||
typedef TransportButton _inherited;
|
||||
};
|
||||
|
||||
|
||||
class RecordButton : public TransportButton {
|
||||
// Knows about recording states, blinks
|
||||
// the recording LED during recording state
|
||||
public:
|
||||
RecordButton(BRect frame, const char *name,
|
||||
BMessage *invokeMessage, // done pressing over button
|
||||
BMessage *blinkMessage = 0, // blinking
|
||||
uint32 key = 0, // optional shortcut key
|
||||
uint32 modifiers = 0, // optional shortcut key modifier
|
||||
uint32 resizeFlags = B_FOLLOW_LEFT | B_FOLLOW_TOP);
|
||||
|
||||
// These need get called periodically to update the button state
|
||||
// OK to call them over and over - once the state is correct, the call
|
||||
// is very low overhead
|
||||
void SetStopped();
|
||||
void SetRecording();
|
||||
|
||||
protected:
|
||||
|
||||
virtual uint32 ModeMask() const;
|
||||
virtual const unsigned char *BitsForMask(uint32) const;
|
||||
|
||||
virtual void StartPressing();
|
||||
virtual void MouseCancelPressing();
|
||||
virtual void DonePressing();
|
||||
|
||||
private:
|
||||
enum RecordState {
|
||||
kAboutToStop,
|
||||
kStopped,
|
||||
kAboutToRecord,
|
||||
kRecordingLedOn,
|
||||
kRecordingLedOff
|
||||
};
|
||||
|
||||
enum {
|
||||
kRecordingMask = 0x4
|
||||
};
|
||||
|
||||
enum {
|
||||
RECORD_PRESSING = 'crpr'
|
||||
};
|
||||
|
||||
RecordState fState;
|
||||
uint32 fLastModeMask;
|
||||
BMessageRunner *fRunner;
|
||||
BMessage *fBlinkMessage;
|
||||
|
||||
typedef TransportButton _inherited;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 "UpDownButton.h"
|
||||
#include "icon_button.h"
|
||||
|
||||
UpDownButton::UpDownButton(BRect rect, BMessage *msg, uint32 resizeFlags)
|
||||
: BControl(rect, "button", NULL, msg, resizeFlags, B_WILL_DRAW),
|
||||
fLastValue(B_CONTROL_ON)
|
||||
{
|
||||
BRect rect = BRect(0, 0, kUpDownButtonWidth - 1, kUpDownButtonHeight - 1);
|
||||
fBitmapUp = new BBitmap(rect, B_CMAP8);
|
||||
fBitmapUp->SetBits(kButtonUpBits, kUpDownButtonWidth * kUpDownButtonHeight, 0, B_CMAP8);
|
||||
fBitmapDown = new BBitmap(rect, B_CMAP8);
|
||||
fBitmapDown->SetBits(kButtonDownBits, kUpDownButtonWidth * kUpDownButtonHeight, 0, B_CMAP8);
|
||||
fBitmapMiddle = new BBitmap(rect, B_CMAP8);
|
||||
fBitmapMiddle->SetBits(kButtonMiddleBits, kUpDownButtonWidth * kUpDownButtonHeight, 0, B_CMAP8);
|
||||
}
|
||||
|
||||
|
||||
UpDownButton::~UpDownButton()
|
||||
{
|
||||
delete fBitmapUp;
|
||||
delete fBitmapDown;
|
||||
delete fBitmapMiddle;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UpDownButton::Draw(BRect updateRect)
|
||||
{
|
||||
SetDrawingMode(B_OP_OVER);
|
||||
|
||||
if(IsTracking()) {
|
||||
if((Bounds().top + Bounds().Height()/2) > (fTrackingY + 3))
|
||||
DrawBitmap(fBitmapUp);
|
||||
else if((Bounds().top + Bounds().Height()/2) < (fTrackingY - 3))
|
||||
DrawBitmap(fBitmapDown);
|
||||
else
|
||||
DrawBitmap(fBitmapMiddle);
|
||||
} else {
|
||||
if(Value()==B_CONTROL_OFF)
|
||||
DrawBitmap(fBitmapUp);
|
||||
else
|
||||
DrawBitmap(fBitmapDown);
|
||||
}
|
||||
|
||||
SetDrawingMode(B_OP_COPY);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UpDownButton::MouseDown(BPoint point)
|
||||
{
|
||||
if(!IsEnabled())
|
||||
return;
|
||||
|
||||
fLastValue = Value();
|
||||
fTrackingY = (Bounds().top + Bounds().Height()/2);
|
||||
|
||||
SetTracking(true);
|
||||
SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
|
||||
|
||||
SetValue(Value() == B_CONTROL_ON ? B_CONTROL_OFF : B_CONTROL_ON);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UpDownButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
|
||||
{
|
||||
if (!IsTracking())
|
||||
return;
|
||||
|
||||
fTrackingY = point.y;
|
||||
Draw(Bounds());
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UpDownButton::MouseUp(BPoint point)
|
||||
{
|
||||
if (!IsTracking())
|
||||
return;
|
||||
|
||||
if((Bounds().top + Bounds().Height()/2) > (fTrackingY + 3))
|
||||
SetValue(B_CONTROL_ON);
|
||||
else if((Bounds().top + Bounds().Height()/2) < (fTrackingY - 3))
|
||||
SetValue(B_CONTROL_OFF);
|
||||
|
||||
if(Value()!=fLastValue)
|
||||
Invoke();
|
||||
SetTracking(false);
|
||||
Draw(Bounds());
|
||||
Flush();
|
||||
fLastValue = Value();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
#ifndef UPDOWNBUTTON_H
|
||||
#define UPDOWNBUTTON_H
|
||||
|
||||
#include <Bitmap.h>
|
||||
#include <Control.h>
|
||||
|
||||
#define DRAG_ITEM 'dndi'
|
||||
|
||||
class UpDownButton : public BControl
|
||||
{
|
||||
public:
|
||||
UpDownButton(BRect rect, BMessage *msg, uint32 resizeFlags = 0);
|
||||
~UpDownButton();
|
||||
virtual void Draw(BRect);
|
||||
virtual void MouseDown(BPoint point);
|
||||
virtual void MouseMoved(BPoint point, uint32 transit, const BMessage *message);
|
||||
virtual void MouseUp(BPoint point);
|
||||
|
||||
private:
|
||||
BBitmap *fBitmapUp, *fBitmapDown, *fBitmapMiddle;
|
||||
float fTrackingY;
|
||||
int32 fLastValue;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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 <Screen.h>
|
||||
#include <Window.h>
|
||||
#include "DrawingTidbits.h"
|
||||
#include "VUView.h"
|
||||
|
||||
const rgb_color back_color = {12, 36, 12};
|
||||
const rgb_color low_color = {40, 120, 40};
|
||||
const rgb_color high_color = {240, 255, 240};
|
||||
|
||||
VUView::VUView(BRect rect, uint32 resizeFlags)
|
||||
: BView(rect, "vumeter", resizeFlags, B_WILL_DRAW),
|
||||
fThreadId(-1),
|
||||
fBitmap(NULL),
|
||||
fQuitting(false)
|
||||
{
|
||||
fLevelCount = int(rect.Height()) / 2;
|
||||
fChannels = 2;
|
||||
fCurrentLevels = new int32[fChannels];
|
||||
for (int channel=0; channel < fChannels; channel++)
|
||||
fCurrentLevels[channel] = 0;
|
||||
fBitmap = new BBitmap(rect, BScreen().ColorSpace(), true);
|
||||
|
||||
|
||||
memset(fBitmap->Bits(), 0, fBitmap->BitsLength());
|
||||
|
||||
fBitmapView = new BView(rect, "bitmapView", B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW);
|
||||
fBitmap->AddChild(fBitmapView);
|
||||
}
|
||||
|
||||
|
||||
VUView::~VUView()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VUView::AttachedToWindow()
|
||||
{
|
||||
SetViewColor(B_TRANSPARENT_COLOR);
|
||||
Run();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VUView::DetachedFromWindow()
|
||||
{
|
||||
Quit();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VUView::Draw(BRect updateRect)
|
||||
{
|
||||
DrawBitmap(fBitmap);
|
||||
|
||||
Sync();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VUView::Run()
|
||||
{
|
||||
fThreadId = spawn_thread(&RenderLaunch, "VU view", B_NORMAL_PRIORITY, this);
|
||||
if (fThreadId < 0)
|
||||
return;
|
||||
resume_thread(fThreadId);
|
||||
}
|
||||
|
||||
void
|
||||
VUView::Quit()
|
||||
{
|
||||
fQuitting = true;
|
||||
snooze(10000);
|
||||
kill_thread(fThreadId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32
|
||||
VUView::RenderLaunch(void *data)
|
||||
{
|
||||
VUView *vu = (VUView*) data;
|
||||
vu->RenderLoop();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
#define SHIFT_UNTIL(value,shift,min) value = (value - shift > min) ? (value - shift) : min
|
||||
|
||||
void
|
||||
VUView::RenderLoop()
|
||||
{
|
||||
rgb_color levels[fLevelCount][2];
|
||||
|
||||
for (int32 i=0; i<fLevelCount; i++) {
|
||||
levels[i][0] = levels[i][1] = back_color;
|
||||
}
|
||||
|
||||
int32 level = 0;
|
||||
|
||||
while (!fQuitting) {
|
||||
|
||||
/* computing */
|
||||
for (int32 channel = 0; channel < 2; channel++) {
|
||||
level = fCurrentLevels[channel];
|
||||
for (int32 i=0; i<level; i++) {
|
||||
if (levels[i][channel].red >= 90) {
|
||||
SHIFT_UNTIL(levels[i][channel].red, 15, low_color.red);
|
||||
SHIFT_UNTIL(levels[i][channel].blue, 15, low_color.blue);
|
||||
} else {
|
||||
SHIFT_UNTIL(levels[i][channel].red, 7, low_color.red);
|
||||
SHIFT_UNTIL(levels[i][channel].blue, 7, low_color.blue);
|
||||
SHIFT_UNTIL(levels[i][channel].green, 14, low_color.green);
|
||||
}
|
||||
}
|
||||
|
||||
levels[level][channel] = high_color;
|
||||
|
||||
for (int32 i=level+1; i<fLevelCount; i++) {
|
||||
if (levels[i][channel].red >= 85) {
|
||||
SHIFT_UNTIL(levels[i][channel].red, 15, back_color.red);
|
||||
SHIFT_UNTIL(levels[i][channel].blue, 15, back_color.blue);
|
||||
} else {
|
||||
SHIFT_UNTIL(levels[i][channel].red, 7, back_color.red);
|
||||
SHIFT_UNTIL(levels[i][channel].blue, 7, back_color.blue);
|
||||
SHIFT_UNTIL(levels[i][channel].green, 14, back_color.green);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* rendering */
|
||||
fBitmap->Lock();
|
||||
fBitmapView->BeginLineArray(fLevelCount * 2);
|
||||
BPoint start1, end1, start2, end2;
|
||||
start1.x = 1;
|
||||
start2.x = 20;
|
||||
end1.x = 14;
|
||||
end2.x = 33;
|
||||
start1.y = end1.y = start2.y = end2.y = 2;
|
||||
for (int32 i=fLevelCount-1; i>=0; i--) {
|
||||
fBitmapView->AddLine(start1, end1, levels[i][0]);
|
||||
fBitmapView->AddLine(start2, end2, levels[i][1]);
|
||||
start1.y = end1.y = start2.y = end2.y = end2.y + 2;
|
||||
}
|
||||
fBitmapView->EndLineArray();
|
||||
fBitmap->Unlock();
|
||||
|
||||
/* ask drawing */
|
||||
|
||||
if (Window()->LockWithTimeout(5000) == B_OK) {
|
||||
Invalidate();
|
||||
Window()->Unlock();
|
||||
snooze(50000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VUView::ComputeNextLevel(void *data, size_t size)
|
||||
{
|
||||
int16* samp = (int16*)data;
|
||||
|
||||
for (int32 channel = 0; channel < fChannels; channel++) {
|
||||
|
||||
// get the min and max values in the nibbling interval
|
||||
// and set max to be the greater of the absolute value
|
||||
// of these.
|
||||
|
||||
int mi = 0, ma = 0;
|
||||
for (uint32 ix=channel; ix<size/sizeof(uint16); ix += fChannels) {
|
||||
if (mi > samp[ix]) mi = samp[ix];
|
||||
else if (ma < samp[ix]) ma = samp[ix];
|
||||
}
|
||||
if (-ma > mi) ma = (mi == -32768) ? 32767 : -mi;
|
||||
|
||||
uint8 n = ma / (2 << (16-7));
|
||||
|
||||
fCurrentLevels[channel] = n;
|
||||
if (fCurrentLevels[channel] < 0)
|
||||
fCurrentLevels[channel] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#ifndef VUVIEW_H
|
||||
#define VUVIEW_H
|
||||
|
||||
#include <Bitmap.h>
|
||||
#include <View.h>
|
||||
|
||||
|
||||
class VUView : public BView
|
||||
{
|
||||
public:
|
||||
VUView(BRect rect, uint32 resizeFlags);
|
||||
~VUView();
|
||||
void AttachedToWindow();
|
||||
void DetachedFromWindow();
|
||||
void Draw(BRect updateRect);
|
||||
void ComputeNextLevel(void *data, size_t size);
|
||||
|
||||
private:
|
||||
void Run();
|
||||
void Quit();
|
||||
static int32 RenderLaunch(void *data);
|
||||
void RenderLoop();
|
||||
|
||||
thread_id fThreadId;
|
||||
BBitmap *fBitmap;
|
||||
BView *fBitmapView;
|
||||
bool fQuitting;
|
||||
int32 fLevelCount;
|
||||
int32 *fCurrentLevels;
|
||||
int32 fChannels;
|
||||
};
|
||||
|
||||
#endif /* VUVIEW_H */
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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 "VolumeSlider.h"
|
||||
#include "icon_button.h"
|
||||
|
||||
#define VOLUME_CHANGED 'vlcg'
|
||||
#define RATIO 2.0f
|
||||
|
||||
VolumeSlider::VolumeSlider(BRect rect, const char *title, uint32 resizeFlags)
|
||||
: BControl(rect, "slider", NULL, new BMessage(VOLUME_CHANGED), resizeFlags, B_WILL_DRAW),
|
||||
fLeftBitmap(BRect(0, 0, kLeftVolumeWidth - 1, kLeftVolumeHeight - 1), B_CMAP8),
|
||||
fRightBitmap(BRect(0, 0, kRightVolumeWidth - 1, kRightVolumeHeight - 1), B_CMAP8),
|
||||
fButtonBitmap(BRect(0, 0, kThumbWidth - 1, kThumbHeight - 1), B_CMAP8),
|
||||
fSoundPlayer(NULL)
|
||||
{
|
||||
fLeftBitmap.SetBits(kLeftVolumeBits, kLeftVolumeWidth * kLeftVolumeHeight, 0, B_CMAP8);
|
||||
fRightBitmap.SetBits(kRightVolumeBits, kRightVolumeWidth * kRightVolumeHeight, 0, B_CMAP8);
|
||||
fButtonBitmap.SetBits(kThumbBits, kThumbWidth * kThumbHeight, 0, B_CMAP8);
|
||||
|
||||
fRight = Bounds().right - 15;
|
||||
}
|
||||
|
||||
|
||||
VolumeSlider::~VolumeSlider()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VolumeSlider::Draw(BRect updateRect)
|
||||
{
|
||||
SetHighColor(189,186,189);
|
||||
StrokeLine(BPoint(11,1), BPoint(fRight,1));
|
||||
SetHighColor(0,0,0);
|
||||
StrokeLine(BPoint(11,2), BPoint(fRight,2));
|
||||
SetHighColor(255,255,255);
|
||||
StrokeLine(BPoint(11,14), BPoint(fRight,14));
|
||||
SetHighColor(231,227,231);
|
||||
StrokeLine(BPoint(11,15), BPoint(fRight,15));
|
||||
|
||||
SetLowColor(ViewColor());
|
||||
|
||||
SetDrawingMode(B_OP_OVER);
|
||||
|
||||
DrawBitmapAsync(&fLeftBitmap, BPoint(5,1));
|
||||
DrawBitmapAsync(&fRightBitmap, BPoint(fRight + 1,1));
|
||||
|
||||
float position = 11 + (fRight - 11) * (fSoundPlayer ? fSoundPlayer->Volume() / RATIO : 0);
|
||||
SetHighColor(102,152,102);
|
||||
FillRect(BRect(11,3,position,4));
|
||||
SetHighColor(152,203,152);
|
||||
FillRect(BRect(11,5,position,13));
|
||||
|
||||
if (fSoundPlayer)
|
||||
SetHighColor(152,152,152);
|
||||
else
|
||||
SetHighColor(200,200,200);
|
||||
FillRect(BRect(position,3,fRight,13));
|
||||
|
||||
SetHighColor(102,152,102);
|
||||
for (int i=15; i<=fRight + 1; i+=5) {
|
||||
if (i>position)
|
||||
SetHighColor(128,128,128);
|
||||
StrokeLine(BPoint(i,8), BPoint(i,9));
|
||||
}
|
||||
|
||||
DrawBitmapAsync(&fButtonBitmap, BPoint(position-5,3));
|
||||
|
||||
Sync();
|
||||
|
||||
SetDrawingMode(B_OP_COPY);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VolumeSlider::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
|
||||
{
|
||||
if (!IsTracking())
|
||||
return;
|
||||
|
||||
uint32 mouseButtons;
|
||||
BPoint where;
|
||||
GetMouse(&where, &mouseButtons, true);
|
||||
|
||||
// button not pressed, exit
|
||||
if (! (mouseButtons & B_PRIMARY_MOUSE_BUTTON)) {
|
||||
Invoke();
|
||||
SetTracking(false);
|
||||
}
|
||||
|
||||
if (!fSoundPlayer || !Bounds().InsetBySelf(2,2).Contains(point))
|
||||
return;
|
||||
|
||||
UpdateVolume(point);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VolumeSlider::MouseDown(BPoint point)
|
||||
{
|
||||
if (!fSoundPlayer || !Bounds().InsetBySelf(2,2).Contains(point))
|
||||
return;
|
||||
|
||||
UpdateVolume(point);
|
||||
SetTracking(true);
|
||||
SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VolumeSlider::MouseUp(BPoint point)
|
||||
{
|
||||
if (!IsTracking())
|
||||
return;
|
||||
if (fSoundPlayer && Bounds().InsetBySelf(2,2).Contains(point)) {
|
||||
UpdateVolume(point);
|
||||
}
|
||||
|
||||
Invoke();
|
||||
SetTracking(false);
|
||||
Draw(Bounds());
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VolumeSlider::UpdateVolume(BPoint point)
|
||||
{
|
||||
fVolume = MIN(MAX(point.x, 11), fRight);
|
||||
fVolume = (fVolume - 11) / (fRight - 11);
|
||||
fVolume = MAX(MIN(fVolume,1), 0);
|
||||
Draw(Bounds());
|
||||
Flush();
|
||||
if (fSoundPlayer)
|
||||
fSoundPlayer->SetVolume(fVolume * RATIO);
|
||||
}
|
||||
|
||||
void
|
||||
VolumeSlider::SetSoundPlayer(BSoundPlayer *player)
|
||||
{
|
||||
fSoundPlayer = player;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
SpeakerView::SpeakerView(BRect rect, uint32 resizeFlags)
|
||||
: BBox(rect, "speaker", resizeFlags, B_WILL_DRAW, B_NO_BORDER),
|
||||
fSpeakerBitmap(BRect(0, 0, kSpeakerIconBitmapWidth - 1, kSpeakerIconBitmapHeight - 1), B_CMAP8)
|
||||
{
|
||||
fSpeakerBitmap.SetBits(kSpeakerIconBits, kSpeakerIconBitmapWidth * kSpeakerIconBitmapHeight, 0, B_CMAP8);
|
||||
}
|
||||
|
||||
|
||||
SpeakerView::~SpeakerView()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SpeakerView::Draw(BRect updateRect)
|
||||
{
|
||||
SetDrawingMode(B_OP_OVER);
|
||||
|
||||
DrawBitmap(&fSpeakerBitmap);
|
||||
|
||||
SetDrawingMode(B_OP_COPY);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
#ifndef VOLUMESLIDER_H
|
||||
#define VOLUMESLIDER_H
|
||||
|
||||
#include <Control.h>
|
||||
#include <Bitmap.h>
|
||||
#include <Box.h>
|
||||
#include <SoundPlayer.h>
|
||||
|
||||
class VolumeSlider : public BControl
|
||||
{
|
||||
public:
|
||||
VolumeSlider(BRect rect, const char* title, uint32 resizeFlags);
|
||||
~VolumeSlider();
|
||||
virtual void Draw(BRect);
|
||||
virtual void MouseMoved(BPoint point, uint32 transit, const BMessage *message);
|
||||
virtual void MouseUp(BPoint point);
|
||||
virtual void MouseDown(BPoint point);
|
||||
void SetSoundPlayer(BSoundPlayer *player);
|
||||
private:
|
||||
void UpdateVolume(BPoint point);
|
||||
BBitmap fLeftBitmap, fRightBitmap, fButtonBitmap;
|
||||
float fRight;
|
||||
float fVolume;
|
||||
BSoundPlayer *fSoundPlayer;
|
||||
};
|
||||
|
||||
class SpeakerView : public BBox
|
||||
{
|
||||
public:
|
||||
SpeakerView(BRect rect, uint32 resizeFlags);
|
||||
~SpeakerView();
|
||||
void Draw(BRect updateRect);
|
||||
|
||||
private:
|
||||
BBitmap fSpeakerBitmap;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
/*******************************************************************************
|
||||
/
|
||||
/ File: array_delete.h
|
||||
/
|
||||
/ Description: Template for deleting a new[] array of something.
|
||||
/
|
||||
/ Copyright 1998-1999, Be Incorporated, All Rights Reserved
|
||||
/
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#if !defined( _array_delete_h )
|
||||
#define _array_delete_h
|
||||
|
||||
// Oooh! It's a template!
|
||||
template<class C> class array_delete {
|
||||
C * & m_ptr;
|
||||
public:
|
||||
// auto_ptr<> uses delete, not delete[], so we have to write our own.
|
||||
// I like hanging on to a reference, because if we manually delete the
|
||||
// array and set the pointer to NULL (or otherwise change the pointer)
|
||||
// it will still work. Others like the more elaborate implementation
|
||||
// of auto_ptr<>. Your Mileage May Vary.
|
||||
array_delete(C * & ptr) : m_ptr(ptr) {}
|
||||
~array_delete() { delete[] m_ptr; }
|
||||
};
|
||||
|
||||
|
||||
#endif /* array_delete_h */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user