[openbeos] New Newsletter Available...

  • From: "Michael Phipps" <mphipps1@xxxxxxxxxxxxxxxx>
  • To: openbeos@xxxxxxxxxxxxx
  • Date: Wed, 30 Oct 2002 23:59:38 -0500

In glorious ASCII text, for your reading enjoyment...

Newsletter Issue: 29
# In This Issue: An Introduction to Inline Source Code Documentation with 
Doxygen by Tyler Dauwalder
# I Will Survive by Stephan Aßmus
# Get Involved by Michael Phipps
An Introduction to Inline Source Code Documentation with Doxygen
by Tyler Dauwalder [tyler@xxxxxxxxxxxxx]

If you're like me, the first time you learn what inline source code 
documentation is, you think "oh super, comments that take me even longer to 
write and make me learn some new form of markup; thank you, no." After dwelling 
on the idea a little more following my initial reaction, however, I warmed up 
to it enough to convince myself to give it a shot. And the rest, as they say, 
is history....

If you look closely through the Storage Kit directories in the OpenBeOS source 
tree, you'll notice an interesting commonality amongst nearly all of our 
function definitions: the vast majority of them are preceded by a funny looking 
/*! */ style comment containing a chunk of descriptive text and a bunch of 
backslashes floating around in odd places. That, my friend, is inline source 
code documentation. Dilligent masochists that we are, most of the Storage Kit's 
public and private APIs are documented using Doxygen, a program that reads 
specially formatted comments in your code and converts them into a big 
organized mess of uber-cross-referenced documentation.

For those in the peanut gallery who remain unconvinced, I suggest you go 
download Doxygen 1.2.1 (yes, I realize this is not the latest posted release, 
but the two other releases are just minor version updates, and this version 
produces the most asthetically pleasing output of the three :-), install it on 
your system, grab a copy of our CVS tree 
(http://cvs.sourceforge.net/cvstarballs/open-beos-cvsroot.tar.gz should do the 
trick, but be warned, it's 9MB or so :-), and march on over to the 
current/docs/develop/storage/ directory therein. One you arrive, run the 
command 'doxygen doxygen_config' from a Terminal session, and watch as a bunch 
of garbage screams past. After things have settled down, you ought to have a 
shiny new current/docs/develop/storage/html/index.html file waiting to be 
opened. Open it.

Assuming everything worked properly, you will have before you the automagically 
generated OpenBeOS Storage Kit Documentation. The main page is pretty boring, 
but the list of links at the top is where the real excitement lies. The Class 
Hierarchy link leads to what I consider to be the most instantly useful page, 
but I encourage you to nose around the various other pages as well. There's a 
ton of information in there! All sorts of crazy cross referencing that required 
next to zero extra effort on our part. Inheritance diagrams, too. I've been 
using this thing for nine months now and it still amazes me.

Now if you browse around long enough, you'll probably start to notice a number 
of things that could be significantly improved. Not all functions have their 
parameters and return values completely and consistently documented. Everything 
is just lumped together, whether private or public. Some of the inheritance 
diagrams tend to blend with the grey background. And though reasonably 
complete, there are notable pieces that have been ignored. One important thing 
you need to realize here, however, is that I have no idea what I'm doing with 
respect to configuring Doxygen. The doxygen_config file we're using is a 
marginally modified version of the provided default template.

Further, masocists though we may be, we're also quite lazy at heart (I am at 
least; I think Ingo actually documents everything :-P), and thus certain things 
that don't seem terribly imporant at the time are, on occasion, ignored and 
left undocumented. The point I'm hoping to get across here, however, is that 
this is still a highly useful collection of information, in spite of it being 
created only through the efforts of lazy programmers who hate documenting their 
work (though it's worth noting that it grows on you the more you do it).

Just think of what a great OpenBeBook we could have if most of the programmers 
involved invested a little extra time to even partially document their work as 
they went, and then somebody or a group of somebodies who really knew what they 
were doing with respect to both documentation in general and doxygen in 
specific dug in, filled in the cracks, and otherwise made things look 
professional. I'd be willing to bet that a properly configured (and possibly 
slightly modified) copy of Doxygen could generate something very asthetically 
similar to the BeBook we all know and love, but with the added bonus of having 
all content and cross references maintained and updated automatically as the 
source code changes. Plus we'd have the option of including the various extra 
indicies and diagrams Doxygen can produce. This may not be a big deal now while 
we're simply recreating BeOS R5, but once OpenBeOS R1 is out and we start to 
move beyond it, new and relatively easily maintainable document!
ation is going to become increasingly important.

So anyway, evangalism aside, those of you who are interested in what I'm 
getting at are probably wondering just where to begin. Well, for you 
self-reliant types, I would suggest wandering over to the Doxygen web site and 
checking out their online manual. All in all, it's pretty helpful and should 
get you going relatively painlessly. Combine that with the examples littered 
throughout the Storage Kit source code, and you should be pretty well off. 
Otherwise, wait here with bated breath and I shall return next time (or maybe 
the time after that... ;-) with the best crash course in Doxygen use that I can 
possibly distill from my limited pool of knowledge on the subject. Until then...
I Will Survive
by Stephan Aßmus [stippi@xxxxxxxxxxxxxxx]

It's not something that might happen in a rare circumstance, something that can 
be neglected in the design of your media application, but something that will 
happen as soon as the user hits that big inviting button on front of the Media 
preference panel - the media_server quitting while you rely on it and the 
connections you have established with your own and other media nodes. So, for 
your programs to survive this situation, is quite desirable. And, of course, 
Soundplay taught us first - it can be done!

The first step towards that goal is detecting a media_server restart at all. It 
cannot be done through any of the event listening methods that the BMediaRoster 
already provides. You have to look elsewhere - another roster perhaps? You 
register one of your loopers with the application roster to receive notices 
whenever an application is launched or quit. In the following example, I will 
use the BApplication object. From there, I will broadcast the event of a 
media_server restart to all parts of the application using media nodes. The 
best place to register with the application roster is probably 
BApplication::ReadyToRun() like this:

void
ExampleApp::ReadyToRun()
{
    // Now tell the application roster, that we're interested
    // in getting notifications of apps being launched or quit.
    // In this way we are going to detect a media_server restart.
    be_roster->StartWatching(BMessenger(this, this), B_REQUEST_LAUNCHED | 
B_REQUEST_QUIT);
    // we will keep track of the status of media_server and media_addon_server
    fMediaServerRunning = 
be_roster->IsRunning("application/x-vnd.Be.media-server");
    fMediaAddOnServerRunning = 
be_roster->IsRunning("application/x-vnd.Be.addon-host");
}

The ExampleApp class has two flags keeping track of the status of both the 
media_sever and media_addon_server. The reason is simply that these are two 
seperate applications, but equally vital for the media kit to be valid, and we 
don't want to take any actions, before they're not both running or have both 
died.

BTW, you might want to unregister with the roster too, most likely in 
QuitRequested().

bool
ExampleApp::QuitRequested()
{
    bool quit = false;
    // find out if we can quit here
    // ...
    if (quit)
        be_roster->StopWatching(BMessenger(this, this));
    return quit;
}

The messages notifying us of the requested events will appear in our 
MessageReceived() method. Though we receive messages for every application 
launching or quitting, we're only interested in the two servers.

void
ExampleApp::MessageReceived(BMessage* msg)
{
    switch (msg->what) {
        case B_SOME_APP_LAUNCHED:
        case B_SOME_APP_QUIT: {
            const char* mimeSig;
            if (msg->FindString("be:signature", &mimeSig) == B_OK) {
                bool isMediaServer = strcmp(mimeSig, 
"application/x-vnd.Be.media-server") == 0;
                bool isAddonServer = strcmp(mimeSig, 
"application/x-vnd.Be.addon-host") == 0;
                if (isMediaServer)
                    fMediaServerRunning = (msg->what == B_SOME_APP_LAUNCHED);
                if (isAddonServer)
                    fMediaAddOnServerRunning = (msg->what == 
B_SOME_APP_LAUNCHED);
                if (isMediaServer || isAddonServer)
                    if (!fMediaServerRunning && !fMediaAddOnServerRunning) {
                        fprintf(stderr, "media server has quit.\n");
                        // everybody can start cleaning up their media nodes
                        BMessage quitMessage(MSG_MEDIA_SERVER_QUIT);
                        BroadcastMessage(&quitMessage);
                    } else if (fMediaServerRunning && fMediaAddOnServerRunning) 
{
                        fprintf(stderr, "media server was launched.\n");
                        // 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) {
                            roster->Lock();
                            roster->Quit();
                        }
                        // give the servers some time to init...
                        snooze(3000000);
                        // tell everybody to re-init their media nodes
                        BMessage launchedMessage(MSG_MEDIA_SERVER_LAUNCHED);
                        BroadcastMessage(&launchedMessage);
                    }
            }
            break;
        }
        default:
            BApplication::MessageReceived(msg);
            break;
    }
}

Our sample application has a method BroadcastMessage(), that simply sends a 
message to all of it's document windows or something of that kind. We just 
assume, that your application supports multiple instances of whatever it has 
for a media node setup. All of those need to be notified in a centralized way, 
which our application object takes care of.

Alright, this code above has one interesting bit. It quits the media roster 
instance, of which there is probably one running in your application team if 
you interacted with the media_server in any way. This object is just your 
ordinary BLooper. If by design or for other reasons remains pure speculation at 
this point in time - this object is of no use anymore, toast so to speak, as 
soon as the media_sever has been restarted. In another words, your previous 
connection with the server is broken. So you need a new one, which will be 
created for you the next time you call BMediaRoster::Roster(). The implications 
are this: what do you do with your invalid nodes and connections? For system 
nodes, you can't do anything. They will most likely be gone already. 
Fortunately, you can get rid of nodes without the roster. BMediaNode::Release() 
will serve this purpose just fine. However, you should not attempt to break the 
connection between nodes, that have been connected before the server!
 died. Or you will be in for a nice little meeting with Rheinmachefrau, and 
that won't be pretty! Actually, the politically correct name for that thread 
shoud have been "Reinigungskraft", but let's not get into that too far. All you 
need in your TearDownNodes() function, is a distinction between a normal clean 
exit, and the dirty one, that needs to follow a media_server breakdown. In the 
dirty case, simply ignore existing connections and system nodes, and generally 
don't use BMediaRoster functions. The other thing to watch out for is locking. 
Since our application could be notified of a media_server shutdown when another 
thread in our team is right in the middle of using the BMediaRoster instance, 
you need to lock the BMediaRoster when you start using it, and unlock it when 
you're done.

An example node cleanup method could thus look like this:

void
ExampleWindow::TearDownNodes(bool disconnect)
{
    // err needs to default to B_OK, since the Roster call only
    // sets it in case of an error
    status_t err = B_OK;
    BMediaRoster* mediaRoster = BMediaRoster::Roster(&err);
    if (err != B_OK) {
        fprintf(stderr, "ExampleWindow::TearDownNodes() - error getting media 
roster: %s\n", strerror(err));
        mediaRoster = NULL;
    }
    // begin mucking with the media roster
    bool mediaRosterLocked = false;
    if (mediaRoster && mediaRoster->Lock())
        mediaRosterLocked = true;

    if (fAudioProducer) {
        status_t err;
        // Ordinarily we'd stop *all* of the nodes in the chain at this point.  
However,
        // one of the nodes is the System Mixer, and stopping the Mixer is a 
Bad Idea (tm).
        // So, we just disconnect from it, and release our references to the 
nodes that
        // we're using.  We *are* supposed to do that even for global nodes 
like the Mixer.
        if (disconnect && mediaRoster) {
            err = mediaRoster->Disconnect(fAudioConnection.producer.node,
                                          fAudioConnection.source,
                                          fAudioConnection.consumer.node,
                                          fAudioConnection.destination);
            if (err != B_OK)
                printf("Error disconnecting audio nodes:  %ld (%s)\n", err, 
strerror(err));
        }

        fAudioProducer->Release();
        if (disconnect && mediaRoster) {
            err = mediaRoster->ReleaseNode(fAudioConnection.consumer);
            if (err != B_OK)
                printf("Error releasing audio consumer: %s\n", strerror(err));
        }
        fAudioProducer = NULL;
        snooze(20000LL);
    }
    // we're done mucking with the media roster
    if (mediaRosterLocked)
        mediaRoster->Unlock();
}

In the above code, fAudioProducer is our own media node object, and it is 
connected to the system mixer. The connection details (endpoints and nodes) are 
stored in a custom connection structure fAudioConnection. Ordinary, 
TearDownNodes() would be called with disconnect = true. Only in the case of a 
media server shutdown that flag is false. Additionally, this method does not 
rely on a valid BMediaRoster instance. But if it's there, it locks the object, 
so that nothing else can interfere.

When the object holding the media node setup, in our example a document window, 
receives the message from the application, that the server has died or started, 
this is what happens:

void
ExampleWindow::MessageReceived(BMessage* msg)
{
    switch (msg->what) {
        case MSG_MEDIA_SERVER_QUIT:
            if (fNodesRunning)
                StopNodes();
            TearDownNodes(false);
            break;
        case MSG_MEDIA_SERVER_LAUNCHED:
            SetupNodes();
            if (fNodesRunning)
                StartNodes();
            break;
        default:
            BWindow::MessageReceived(msg);
            break;
    }
}

fNodesRunning is not, as you might think, representing the run status of our 
node setup, but if we have valid nodes at all. According to this flag, you 
might want to be prepared to give some feedback to the user when something went 
wrong.

So what's missing? The rest of the code, of course, so here it is:

#define ErrorAlert(str, status) printf(str " Error: %s\n", strerror(status))

status_t
ExampleWindow::SetupNodes()
{
    media_raw_audio_format format;
    format = media_raw_audio_format::wildcard;
    format.frame_rate = 44100.0;
    format.channel_count = 2;
    format.format = media_raw_audio_format::B_AUDIO_FLOAT;

    if (B_HOST_IS_BENDIAN)
        format.byte_order = B_MEDIA_BIG_ENDIAN;
    else if (B_HOST_IS_LENDIAN)
        format.byte_order = B_MEDIA_LITTLE_ENDIAN;

    status_t status = B_OK;

    // find the media roster
    BMediaRoster* mediaRoster = BMediaRoster::Roster(&status);
    if (!mediaRoster || status != B_OK) {
        ErrorAlert("Can't find the media roster.", status);
        mediaRoster = NULL;
        return status;
    }
    if (mediaRoster->Lock()) {
        // find the time source
        media_node time_source_node;
        status = mediaRoster->GetTimeSource(&time_source_node);
        if (status != B_OK) {
            ErrorAlert("Can't get a time source.", status);
            mediaRoster->Unlock();
            return status;
        }

        // the AudioProducer connection

        fAudioProducer = new AudioProducer(fChannelManager, this);
        status = mediaRoster->RegisterNode(fAudioProducer);
        if (status != B_OK) {
            ErrorAlert("Unable to register AudioProducer node!", status);
            mediaRoster->Unlock();
            return status;
        }
        fAudioConnection.producer = fAudioProducer->Node();

        // connect to the mixer
        status = mediaRoster->GetAudioMixer(&fAudioConnection.consumer);
        if (status != B_OK) {
            ErrorAlert("Unable to get the system mixer.", status);
            mediaRoster->Unlock();
            return status;
        }

        mediaRoster->SetTimeSourceFor(fAudioConnection.producer.node, 
time_source_node.node);

        // got the nodes; now we find the endpoints of the connection
        media_input mixerInput;
        media_output soundOutput;
        int32 count = 1;
        status = mediaRoster->GetFreeOutputsFor(fAudioConnection.producer, 
&soundOutput, 1, &count);
        if (status != B_OK) {
            ErrorAlert("Unable to get a free output from the producer node.", 
status);
            mediaRoster->Unlock();
            return status;
        }
        count = 1;
        status = mediaRoster->GetFreeInputsFor(fAudioConnection.consumer, 
&mixerInput, 1, &count);
        if (status != B_OK) {
            ErrorAlert("Unable to get a free input to the mixer.", status);
            mediaRoster->Unlock();
            return status;
        }

        // got the endpoints; now we connect it!
        media_format audio_format;
        audio_format.type = B_MEDIA_RAW_AUDIO;
        audio_format.u.raw_audio = media_raw_audio_format::wildcard;
        status = mediaRoster->Connect(soundOutput.source, 
mixerInput.destination, &audio_format, &soundOutput, &mixerInput);
        if (status != B_OK) {
            ErrorAlert("Unable to connect nodes.", status);
            mediaRoster->Unlock();
            return status;
        }

        // the inputs and outputs might have been reassigned during the
        // nodes' negotiation of the Connect().  That's why we wait until
        // after Connect() finishes to save their contents.
        fAudioConnection.format = audio_format;
        fAudioConnection.source = soundOutput.source;
        fAudioConnection.destination = mixerInput.destination;

        // Set an appropriate run mode for the producer
        mediaRoster->SetRunModeNode(fAudioConnection.producer, 
BMediaNode::B_INCREASE_LATENCY);
        // done mucking with the media roster
        mediaRoster->Unlock();
    }
    return status;
}

status_t
ExampleWindow::StartNodes()
{
    status_t status = B_OK;
    BMediaRoster* mediaRoster = BMediaRoster::Roster(&status);
    if (status != B_OK) {
        fprintf(stderr, "ExampleWindow::StartNodes() - error getting media 
roster: %s\n", strerror(status));
        mediaRoster = NULL;
    }

    if (mediaRoster && fAudioProducer)
        status = B_ERROR;  // error returned when locking fails
    if (mediaRoster->Lock()) {
        // figure out what recording delay to use
        bigtime_t latency = 0;
        status = mediaRoster->GetLatencyFor(fAudioProducer->Node(), &latency);
        status = mediaRoster->SetProducerRunModeDelay(fAudioProducer->Node(), 
latency);

        // start the nodes
        bigtime_t init_latency = 0;
        status = mediaRoster->GetInitialLatencyFor(fAudioProducer->Node(), 
&init_latency);
        if (status != B_OK)
            printf("Can't get initial latency for audio producer node - Error: 
%s\n", strerror(status));

        init_latency += estimate_max_scheduling_latency();

        BTimeSource* timeSource = 
mediaRoster->MakeTimeSourceFor(fAudioProducer->Node());

        bigtime_t real = timeSource->RealTime();
        bigtime_t perf = timeSource->PerformanceTimeFor(real + latency + 
init_latency);

        timeSource->Release();

        status = mediaRoster->StartNode(fAudioConnection.producer, perf);
        if (status != B_OK)
            printf("Can't start the audio producer - Error: %s\n", 
strerror(status));
        // done mucking with the media server
        mediaRoster->Unlock();
    }
    return status;
}

void
ExampleWindow::StopNodes()
{
    status_t status = B_OK;
    BMediaRoster* mediaRoster = BMediaRoster::Roster(&status);
    if (status != B_OK) {
        fprintf(stderr, "ExampleWindow::StopNodes() - error getting media 
roster: %s\n", strerror(status));
        mediaRoster = NULL;
    }
    if (fAudioProducer && mediaRoster && mediaRoster->Lock()) {
        mediaRoster->StopNode(fAudioConnection.producer, 0, true);        // 
synchronous stop
        mediaRoster->Unlock();
    }
}

Alright, I hope this article gets you up and running handling a media_server 
restart. If you have anymore questions, feel free to contact me via email. Best 
of luck!
Get Involved
by Michael Phipps [michaelphipps@xxxxxxxxxxxxxxxxxxxxx]

Get Involved

There are two topics that, according to the old saw, you should never discuss 
in polite company: Religion and Politics. This time of year, though, in the 
United States, you have no choice but to hear about politics. Day and night, 
night and day, commercial after commercial. This one is no good, that one lies, 
the other one is stealing from you.

It is enough to drive you nuts. I want to contemplate, this week, why there is 
so much name-calling and back-stabbing. I think that it is time for all 
Americans (and this really applies to any other country that I know of, too, 
but I will talk about that which I know) to reflect a little bit on their 
political system and why they should be more involved. I want to do this, 
though, without discussing my particular view on the issues. :-)

Voter turnout has reached record lows over the past 10 years or so. This is a 
symptom of a larger issue. People, in general, don't think that they have an 
effect on government. They forget that politicians love their power and want to 
keep it. They forget that the perception of voters turning against those in 
power can be enough to make a difference. Not that every person should write a 
letter to their particular Congresspeople once a week telling them how to vote 
- that is the quickest route to the crackpot list. An occasional, comprehensive 
single issue paper, backed with some research and, preferably, signed by 
multiple people from their district is far more likely to get by the gatekeeper 
at the office and to the politician's desk. Forget internet polls, marching in 
the street or even calling radio shows to complain.

Everyone should vote. It takes only a few minutes (about 10, where I live). You 
can take a piece of paper with your choices on it and know what you are doing. 
It is really not all that hard and it is a worthwhile activity. If you were, 
say, a librarian that catered to a group of people that accepted whatever you 
did (bought only old, used books at garage sales), how seriously would you take 
your job? Why would you really agonize over how you spend your budget if no one 
cares? You wouldn't -- you might start all full of professional pride and 
desire to do the best that you can, but in a short period of time, the apathy 
of the other workers (who play Unreal all day) and the lack of team spirit will 
bring you down. Voting is the clearest, most obvious signal that we can send to 
say "Hey - we are watching you, we care about our government and if you don't 
do a good job, you can find a different job."

US government is a very interesting topic. One wise commentator recently noted 
that while everyone focuses on the President, his decisions and policies, for 
the most part, don't have anywhere near the impact as that of the governor of 
your state. The governor can and does affect most of the taxes you pay, most of 
the laws that you obey and most of the changes in the economics of your state. 
Certainly the US economy as a whole has some impact on states. But something as 
simple as the tone that a governor sets can make a difference in a whole state. 
County and city governments can make even more difference. The mayor of my city 
has a significant impact on the way that the police and teachers do their jobs; 
this affects citizens far more than most of what goes on in Washington D.C. We 
as citizens need to pay attention to these "minor" races in the "off-year" 
elections. These are the most important elections in our day to day lives.

Finally, I would like to encourage people to get involved directly. Find a 
candidate that you believe in and campaign for them. Volunteer to help at the 
polls. Maybe you could even become a candidate in your town or county. We need 
to show the politicians that we are watching, paying attention and that we care 
about the issues of the day. If we don't, who will?



Other related posts:

  • » [openbeos] New Newsletter Available...