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

View File

@ -19,6 +19,7 @@
#include <Beep.h> #include <Beep.h>
#include <ControlLook.h> #include <ControlLook.h>
#include <Dragger.h> #include <Dragger.h>
#include <MessageRunner.h>
#include <Roster.h> #include <Roster.h>
#include <AppMisc.h> #include <AppMisc.h>
@ -28,17 +29,23 @@
#include "VolumeWindow.h" #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) VolumeControl::VolumeControl(int32 volumeWhich, bool beep, BMessage* message)
: BSlider("VolumeControl", "Volume", message, 0, 1, B_HORIZONTAL), : BSlider("VolumeControl", "Volume", message, 0, 1, B_HORIZONTAL),
fMixerControl(new MixerControl(volumeWhich)),
fBeep(beep), fBeep(beep),
fSnapping(false) fSnapping(false),
fConnectRetries(0)
{ {
font_height fontHeight; font_height fontHeight;
GetFontHeight(&fontHeight); GetFontHeight(&fontHeight);
SetBarThickness(ceilf((fontHeight.ascent + fontHeight.descent) * 0.7)); SetBarThickness(ceilf((fontHeight.ascent + fontHeight.descent) * 0.7));
_InitVolume(volumeWhich);
BRect rect(Bounds()); BRect rect(Bounds());
rect.top = rect.bottom - 7; rect.top = rect.bottom - 7;
rect.left = rect.right - 7; rect.left = rect.right - 7;
@ -49,7 +56,10 @@ VolumeControl::VolumeControl(int32 volumeWhich, bool beep, BMessage* message)
VolumeControl::VolumeControl(BMessage* archive) VolumeControl::VolumeControl(BMessage* archive)
: BSlider(archive) : BSlider(archive),
fMixerControl(NULL),
fSnapping(false),
fConnectRetries(0)
{ {
if (archive->FindBool("beep", &fBeep) != B_OK) if (archive->FindBool("beep", &fBeep) != B_OK)
fBeep = false; fBeep = false;
@ -58,7 +68,7 @@ VolumeControl::VolumeControl(BMessage* archive)
if (archive->FindInt32("volume which", &volumeWhich) != B_OK) if (archive->FindInt32("volume which", &volumeWhich) != B_OK)
volumeWhich = VOLUME_USE_MIXER; volumeWhich = VOLUME_USE_MIXER;
_InitVolume(volumeWhich); fMixerControl = new MixerControl(volumeWhich);
} }
@ -109,10 +119,16 @@ VolumeControl::AttachedToWindow()
else else
SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
BMediaRoster* roster = BMediaRoster::CurrentRoster(); be_roster->StartWatching(this, B_REQUEST_LAUNCHED | B_REQUEST_QUIT);
if (roster != NULL && fMixerControl->GainNode() != media_node::null) {
roster->StartWatching(this, fMixerControl->GainNode(), _ConnectVolume();
B_MEDIA_NEW_PARAMETER_VALUE);
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 void
VolumeControl::DetachedFromWindow() VolumeControl::DetachedFromWindow()
{ {
BMediaRoster* roster = BMediaRoster::CurrentRoster(); _DisconnectVolume();
if (roster != NULL && fMixerControl->GainNode() != media_node::null) {
roster->StopWatching(this, fMixerControl->GainNode(), be_roster->StopWatching(this);
B_MEDIA_NEW_PARAMETER_VALUE);
}
} }
@ -267,6 +281,53 @@ VolumeControl::MessageReceived(BMessage* msg)
"OK"))->Go(NULL); "OK"))->Go(NULL);
break; 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: default:
return BView::MessageReceived(msg); return BView::MessageReceived(msg);
} }
@ -324,11 +385,24 @@ VolumeControl::UpdateText() const
void 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; const char* errorString = NULL;
float volume = 0.0; float volume = 0.0;
fMixerControl = new MixerControl(volumeWhich, &volume, &errorString); fMixerControl->Connect(fMixerControl->VolumeWhich(), &volume, &errorString);
if (errorString != NULL) { if (errorString != NULL) {
SetLabel(errorString); SetLabel(errorString);
@ -337,6 +411,12 @@ VolumeControl::_InitVolume(int32 volumeWhich)
SetLabel("Volume"); SetLabel("Volume");
SetLimits((int32)floorf(fMixerControl->Minimum()), SetLimits((int32)floorf(fMixerControl->Minimum()),
(int32)ceilf(fMixerControl->Maximum())); (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); SetEnabled(errorString == NULL);
@ -355,10 +435,10 @@ VolumeControl::_PointForValue(int32 value) const
if (Orientation() == B_HORIZONTAL) { if (Orientation() == B_HORIZONTAL) {
return ceilf(1.0f * (value - min) / (max - min) return ceilf(1.0f * (value - min) / (max - min)
* (BarFrame().Width() - 2) + BarFrame().left + 1); * (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; virtual const char* UpdateText() const;
private: private:
void _InitVolume(int32 volumeWhich); void _DisconnectVolume();
void _ConnectVolume();
bool _IsReplicant() const; bool _IsReplicant() const;
float _PointForValue(int32 value) const; float _PointForValue(int32 value) const;
@ -54,6 +55,10 @@ private:
bool fSnapping; bool fSnapping;
float fMinSnap; float fMinSnap;
float fMaxSnap; float fMaxSnap;
int32 fConnectRetries;
bool fMediaServerRunning;
bool fAddOnServerRunning;
}; };
#endif // VOLUME_SLIDER_H #endif // VOLUME_SLIDER_H