* With the help of Stippi's "I Will Survive" article, the VolumeControl

replicant should now handle media server restarts, or late starts gracefully.
* This fixed ticket #4002.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31322 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2009-06-30 10:14:54 +00:00
parent 5b60969c5c
commit da30b4bf83
4 changed files with 167 additions and 47 deletions

View File

@ -17,8 +17,7 @@
#include <ParameterWeb.h>
MixerControl::MixerControl(int32 volumeWhich, float* _value,
const char** _error)
MixerControl::MixerControl(int32 volumeWhich)
:
fVolumeWhich(volumeWhich),
fGainMediaNode(media_node::null),
@ -28,12 +27,28 @@ MixerControl::MixerControl(int32 volumeWhich, float* _value,
fMax(0.0f),
fStep(0.0f)
{
}
MixerControl::~MixerControl()
{
_Disconnect();
}
bool
MixerControl::Connect(int32 volumeWhich, float* _value, const char** _error)
{
fVolumeWhich = volumeWhich;
_Disconnect();
bool retrying = false;
status_t err = B_OK;
/* BMediaRoster::Roster() doesn't set it if all is ok */
status_t status = B_OK;
// BMediaRoster::Roster() doesn't set it if all is ok
const char* errorString = NULL;
BMediaRoster* roster = BMediaRoster::Roster(&err);
BMediaRoster* roster = BMediaRoster::Roster(&status);
retry:
// Here we release the BMediaRoster once if we can't access the system
@ -49,22 +64,22 @@ retry:
roster->Quit();
}
snooze(10000);
roster = BMediaRoster::Roster(&err);
roster = BMediaRoster::Roster(&status);
}
if (roster && err == B_OK) {
if (roster != NULL && status == B_OK) {
switch (volumeWhich) {
case VOLUME_USE_MIXER:
err = roster->GetAudioMixer(&fGainMediaNode);
status = roster->GetAudioMixer(&fGainMediaNode);
break;
case VOLUME_USE_PHYS_OUTPUT:
err = roster->GetAudioOutput(&fGainMediaNode);
status = roster->GetAudioOutput(&fGainMediaNode);
break;
}
if (err == B_OK) {
err = roster->GetParameterWebFor(fGainMediaNode, &fParameterWeb);
if (err == B_OK) {
// Finding the Mixer slider in the audio output ParameterWeb
if (status == B_OK) {
status = roster->GetParameterWebFor(fGainMediaNode, &fParameterWeb);
if (status == B_OK) {
// Finding the Mixer slider in the audio output ParameterWeb
int32 numParams = fParameterWeb->CountParameters();
BParameter* p = NULL;
bool foundMixerLabel = false;
@ -102,11 +117,14 @@ retry:
if (!strcmp(p->Kind(), B_MASTER_GAIN)) {
for (; i < numParams; i++) {
p = fParamWeb->ParameterAt(i);
if (strcmp(p->Kind(), B_MASTER_GAIN)) p=NULL;
else break;
if (strcmp(p->Kind(), B_MASTER_GAIN))
p = NULL;
else
break;
}
break;
} else p = NULL;
} else
p = NULL;
#endif
p = NULL;
}
@ -149,7 +167,7 @@ retry:
errorString = "No Media Roster";
}
if (err != B_OK)
if (status != B_OK)
fGainMediaNode = media_node::null;
if (errorString) {
@ -159,16 +177,15 @@ retry:
}
if (fMixerParameter == NULL && _value != NULL)
*_value = 0;
return errorString == NULL;
}
MixerControl::~MixerControl()
bool
MixerControl::Connected()
{
delete fParameterWeb;
BMediaRoster* roster = BMediaRoster::CurrentRoster();
if (roster != NULL && fGainMediaNode != media_node::null)
roster->ReleaseNode(fGainMediaNode);
return fGainMediaNode != media_node::null;
}
@ -220,3 +237,17 @@ MixerControl::ChangeVolumeBy(float value)
SetVolume(volume + value);
}
void
MixerControl::_Disconnect()
{
delete fParameterWeb;
fParameterWeb = NULL;
fMixerParameter = NULL;
BMediaRoster* roster = BMediaRoster::CurrentRoster();
if (roster != NULL && fGainMediaNode != media_node::null)
roster->ReleaseNode(fGainMediaNode);
fGainMediaNode = media_node::null;
}

View File

@ -23,11 +23,13 @@ class BContinuousParameter;
class MixerControl {
public:
MixerControl(int32 volumeWhich,
float* _value = NULL,
const char** _error = NULL);
MixerControl(int32 volumeWhich);
~MixerControl();
bool Connect(int32 volumeWhich, float* _value = NULL,
const char** _error = NULL);
bool Connected();
int32 VolumeWhich() const;
float Volume() const;
@ -40,6 +42,8 @@ public:
media_node GainNode() { return fGainMediaNode; }
private:
void _Disconnect();
int32 fVolumeWhich;
media_node fGainMediaNode;
BParameterWeb* fParameterWeb;

View File

@ -19,6 +19,7 @@
#include <Beep.h>
#include <ControlLook.h>
#include <Dragger.h>
#include <MessageRunner.h>
#include <Roster.h>
#include <AppMisc.h>
@ -28,17 +29,23 @@
#include "VolumeWindow.h"
static const char* kMediaServerSignature = "application/x-vnd.Be.media-server";
static const char* kAddOnServerSignature = "application/x-vnd.Be.addon-host";
static const uint32 kMsgReconnectVolume = 'rcms';
VolumeControl::VolumeControl(int32 volumeWhich, bool beep, BMessage* message)
: BSlider("VolumeControl", "Volume", message, 0, 1, B_HORIZONTAL),
fMixerControl(new MixerControl(volumeWhich)),
fBeep(beep),
fSnapping(false)
fSnapping(false),
fConnectRetries(0)
{
font_height fontHeight;
GetFontHeight(&fontHeight);
SetBarThickness(ceilf((fontHeight.ascent + fontHeight.descent) * 0.7));
_InitVolume(volumeWhich);
BRect rect(Bounds());
rect.top = rect.bottom - 7;
rect.left = rect.right - 7;
@ -49,7 +56,10 @@ VolumeControl::VolumeControl(int32 volumeWhich, bool beep, BMessage* message)
VolumeControl::VolumeControl(BMessage* archive)
: BSlider(archive)
: BSlider(archive),
fMixerControl(NULL),
fSnapping(false),
fConnectRetries(0)
{
if (archive->FindBool("beep", &fBeep) != B_OK)
fBeep = false;
@ -58,7 +68,7 @@ VolumeControl::VolumeControl(BMessage* archive)
if (archive->FindInt32("volume which", &volumeWhich) != B_OK)
volumeWhich = VOLUME_USE_MIXER;
_InitVolume(volumeWhich);
fMixerControl = new MixerControl(volumeWhich);
}
@ -109,10 +119,16 @@ VolumeControl::AttachedToWindow()
else
SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
BMediaRoster* roster = BMediaRoster::CurrentRoster();
if (roster != NULL && fMixerControl->GainNode() != media_node::null) {
roster->StartWatching(this, fMixerControl->GainNode(),
B_MEDIA_NEW_PARAMETER_VALUE);
be_roster->StartWatching(this, B_REQUEST_LAUNCHED | B_REQUEST_QUIT);
_ConnectVolume();
if (!fMixerControl->Connected()) {
// Wait a bit, and try again - the media server might not have been
// ready yet
BMessage reconnect(kMsgReconnectVolume);
BMessageRunner::StartSending(this, &reconnect, 1000000LL, 1);
fConnectRetries = 3;
}
}
@ -120,11 +136,9 @@ VolumeControl::AttachedToWindow()
void
VolumeControl::DetachedFromWindow()
{
BMediaRoster* roster = BMediaRoster::CurrentRoster();
if (roster != NULL && fMixerControl->GainNode() != media_node::null) {
roster->StopWatching(this, fMixerControl->GainNode(),
B_MEDIA_NEW_PARAMETER_VALUE);
}
_DisconnectVolume();
be_roster->StopWatching(this);
}
@ -267,6 +281,53 @@ VolumeControl::MessageReceived(BMessage* msg)
"OK"))->Go(NULL);
break;
case B_SOME_APP_LAUNCHED:
case B_SOME_APP_QUIT:
{
const char* signature;
if (msg->FindString("be:signature", &signature) != B_OK)
break;
bool isMediaServer = !strcmp(signature, kMediaServerSignature);
bool isAddOnServer = !strcmp(signature, kAddOnServerSignature);
if (isMediaServer)
fMediaServerRunning = msg->what == B_SOME_APP_LAUNCHED;
if (isAddOnServer)
fAddOnServerRunning = msg->what == B_SOME_APP_LAUNCHED;
if (isMediaServer || isAddOnServer) {
if (!fMediaServerRunning && !fAddOnServerRunning) {
// No media server around
SetLabel("No media server running");
SetEnabled(false);
} else if (fMediaServerRunning && fAddOnServerRunning) {
// HACK!
// quit our now invalid instance of the media roster
// so that before new nodes are created,
// we get a new roster
BMediaRoster* roster = BMediaRoster::CurrentRoster();
if (roster != NULL) {
roster->Lock();
roster->Quit();
}
BMessage reconnect(kMsgReconnectVolume);
BMessageRunner::StartSending(this, &reconnect, 1000000LL, 1);
fConnectRetries = 3;
}
}
break;
}
case kMsgReconnectVolume:
_ConnectVolume();
if (!fMixerControl->Connected() && --fConnectRetries > 1) {
BMessage reconnect(kMsgReconnectVolume);
BMessageRunner::StartSending(this, &reconnect,
6000000LL / fConnectRetries, 1);
}
break;
default:
return BView::MessageReceived(msg);
}
@ -324,11 +385,24 @@ VolumeControl::UpdateText() const
void
VolumeControl::_InitVolume(int32 volumeWhich)
VolumeControl::_DisconnectVolume()
{
BMediaRoster* roster = BMediaRoster::CurrentRoster();
if (roster != NULL && fMixerControl->GainNode() != media_node::null) {
roster->StopWatching(this, fMixerControl->GainNode(),
B_MEDIA_NEW_PARAMETER_VALUE);
}
}
void
VolumeControl::_ConnectVolume()
{
_DisconnectVolume();
const char* errorString = NULL;
float volume = 0.0;
fMixerControl = new MixerControl(volumeWhich, &volume, &errorString);
fMixerControl->Connect(fMixerControl->VolumeWhich(), &volume, &errorString);
if (errorString != NULL) {
SetLabel(errorString);
@ -337,6 +411,12 @@ VolumeControl::_InitVolume(int32 volumeWhich)
SetLabel("Volume");
SetLimits((int32)floorf(fMixerControl->Minimum()),
(int32)ceilf(fMixerControl->Maximum()));
BMediaRoster* roster = BMediaRoster::CurrentRoster();
if (roster != NULL && fMixerControl->GainNode() != media_node::null) {
roster->StartWatching(this, fMixerControl->GainNode(),
B_MEDIA_NEW_PARAMETER_VALUE);
}
}
SetEnabled(errorString == NULL);
@ -355,10 +435,10 @@ VolumeControl::_PointForValue(int32 value) const
if (Orientation() == B_HORIZONTAL) {
return ceilf(1.0f * (value - min) / (max - min)
* (BarFrame().Width() - 2) + BarFrame().left + 1);
} else {
return ceilf(BarFrame().top - 1.0f * (value - min) / (max - min)
* BarFrame().Height());
}
return ceilf(BarFrame().top - 1.0f * (value - min) / (max - min)
* BarFrame().Height());
}

View File

@ -42,7 +42,8 @@ protected:
virtual const char* UpdateText() const;
private:
void _InitVolume(int32 volumeWhich);
void _DisconnectVolume();
void _ConnectVolume();
bool _IsReplicant() const;
float _PointForValue(int32 value) const;
@ -54,6 +55,10 @@ private:
bool fSnapping;
float fMinSnap;
float fMaxSnap;
int32 fConnectRetries;
bool fMediaServerRunning;
bool fAddOnServerRunning;
};
#endif // VOLUME_SLIDER_H