Issue 3-45, November 11, 1998

Be Engineering Insights: That BeOS is one baaad mother-[Shut your mouth!] ...just talking 'bout BeOS

By George "The Animal" Hoffman

Jesse "The Body" Ventura is now the governor of Minnesota. He has, so far, upped his mark on history from being the only gubernatorial candidate to use the theme from "Shaft" in his campaign ads to also being the only former pro wrestler to be elected governor. To all of us here at Be, Jesse "The Body" is a shining example of a third-party candidate who can beat the odds to win out over the incumbent and deliver what his constituents really want. All it takes is some gumption, a great product, and a fab set of pects. Between JLG and the rest of us here at Be, I think we've got what it takes. Jesse, we salute you!

The app server has been vastly restructured in BeOS Release 4. Or, to put it another way, the restructuring that I began in Release 3 has been largely completed in R4. The app server is about 25,000 lines of code slimmer. Of the remaining code, probably less than 40% of it has not either been modified significantly or replaced with new code. For the graphics team, R4 is certainly the most significant release we've been involved in.

Much as I would love to brag about all the great stuff that's changed internally for the better, I'll restrain myself. Suffice it to say that the R4 app server is fundamentally much more robust, scalable, and sexy. It's also faster—in some extreme cases, several orders of magnitude faster.

That said, the features that we've introduced in R4 are not really so innovative per se. For the most part they're features that other operating systems already have, in one form or another. In this way, R4 has been sort of a "catch-up" release for the app server team, expanding our feature set to better support applications that are coming online to access the performance BeOS gives them.

"Yeah, blah, blah, blah. How about a feature list, dude?" OK. Those of you with copies of the beta, follow along at home! For the rest of you, don't worry, you'll see it soon enough.

Much More Parallel

Some people with old S3 and Cirrus Logic video cards experienced hard system freezes with earlier versions of the R4 beta. These freezes prove that the incredibly buff new R4 graphics driver architecture (designed and implemented by our own Trey "Ball-Buster" Boudreau) is working correctly—too well, in the case of these cards. Prior to R4, the locking done when a view was drawing was coarse-grained; no two threads could draw to the frame buffer at the same time.

In R4, any number of threads can draw to the frame buffer simultaneously. The only resource locked for exclusive access is the acceleration engine, and that is locked only for the time required to feed the rendering commands through the FIFO; synchronizing with the engine is intelligent and is done only when absolutely necessary. This will be of particular use in R5, when we plan to start accelerating many more calls than we do now.

For R4, this means that the graphics system should be far more scalable when using cards that support the new driver model.

Faster Views

BViews, in all previous releases, have been too heavyweight for many applications. A major valid complaint has been that BViews are too slow when there are a large number of them in a window. In R4, views are much faster. Although a full drawing context is still stored with every BView -- and so the memory usage has not decreased—the major bottlenecks in resizing, moving, and updating large numbers of views have been pinned and beaten into submission.

One of those was recalculating the clipping region of the parent view. All applications will benefit from optimizations made to these calculations. For those who need more speed though, there is a new view transaction API. This consists of two methods, BWindow::BeginViewTransaction() and BWindow::EndViewTransaction(). The view system will behave as before without these calls, but if you bracket resizing and moving of views between these two calls, the parent's clipping region is not recalculated until the call to EndViewTransaction().

These calls also do implicit DisableUpdates() and EnableUpdates(). The result is a dramatic increase in performance; the parent's clipping region is updated once, rather than many intermediate times, and only one update event is shot off at the end of the process. It's not uncommon for a window with a large number of sibling views (say, a parent with 500 children) to see a 10x speedup without any changes to the source, and a 100x speedup when using the new API.

It's my explicit intention with the addition of this API to support client-side layout libraries. R5 will probably see a major reduction in memory usage for most views, with pieces of the rendering state allocated only on demand.

Views Can Draw on Children

Another favorite feature request has been to allow parent views to draw on top of their children; i.e., do not exclude the children of a view from that view's clipping region. R4 allows this and provides API to support it.

To enable this behavior in a view, you must set the B_DRAW_ON_CHILDREN flag. (I initially called this flag B_SCRIBBLE_ON_PRESCHOOLERS, but our documentation guy Doug "Hellfire" Fulton nixed that.) Setting this flag has two effects: the view no longer excludes its children from its clipping region; and a new, virtual BView::DrawAfterChildren(), is called after all the children of a view have had their own Draw() methods called on an update. This lets the update mechanism correctly manage this new behavior. For instance, DrawAfterChildren() could draw a line from the center of one child view to the center of another; because it is called after the Draw() methods of those individual views, its drawing goes on top of whatever those views draw.

It's worth noting that using B_TRANSPARENT_COLOR as the view color of a child of a parent that sets this flag allows some interesting effects, previously impossible to achieve with views. For instance, the parent can choose to fill itself with a tiled bitmap pattern, and the child can draw itself with a bitmap using B_OP_OVER or the new B_OP_ALPHA, or even set an arbitrary clipping region and draw. This effectively allows a limited form of non-rectangular views. The only thing missing is the ability to clip the parent around an arbitrary child shape.

Also, a parent region with B_DRAW_ON_CHILDREN enabled never needs to recalculate its clipping region when a child resizes or moves, for obvious reasons. So even if your parent never draws anything, it can be a slight performance win in some cases to set this flag for views with large numbers of children.

Arbitrary Clipping Regions

R4 introduces the long-awaited ability to clip to an arbitrary shape, rather than the resolution-dependent, awkward BRegions. It's now possible to clip to either the region of a BPicture or the inverse of that region. The new BView methods that allow this are called -- startlingly—ClipToPicture() and ClipToInversePicture(). Just build a BPicture the way you normally would and then use it in a call to these methods.

void ClipToPicture(BPicture *picture,
                   BPoint where = B_ORIGIN,
                   bool sync = true);

void ClipToInversePicture(BPicture *picture,
                          BPoint where = B_ORIGIN,
                          bool sync = true);

There are many advantages to using this new clipping API. BPictures are stored as collections of drawing commands. They are thus by nature in a vector format, and scale well for resolution-independent output, such as printing. Because BPictures are server-side objects, the app server can cache the complex arbitrary regions that a BPicture can produce, so using the same BPicture for the same clipping task repeatedly is fairly fast. Also, as a completely intentional surprise bonus, it's easy to create a clipping mask for an object. For instance, I can disable clipping, draw picture A, then call ClipToInversePicture(A) and draw everything "underneath" A.

When your clipping region is simple and resolution dependent (i.e., probably most of the time), it's still a good idea to use BRegion clipping (ConstrainClippingRegion() calls) because they incur much less overhead.

Setting the sync parameter to true guarantees that the call to the app server will complete by the time the method call returns. You should do this if you plan, for instance, to delete your BPicture object afterwards.

The rules for BRegion clipping also apply to picture clipping. A BRegion clipping area replaces a BPicture clipping region within the same state stack level, and vice versa.

Alpha-Blended Rendering

Mmmmm...alpha.

B_OP_ALPHA has been added to the list of drawing modes. This alpha blends all primitives based on several parameters which can be set with the new SetBlendingMode() call:

void SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc);

The possible values for the source alpha source (the source from which the source alpha for the blending operation is obtained) are B_CONSTANT_ALPHA and B_PIXEL_ALPHA. B_CONSTANT_ALPHA tells the app server you want to use the alpha value of the color you're drawing with as the source alpha for the operation. B_PIXEL_ALPHA instead uses the alpha value of the source pixel of the bitmap you're blitting. This is only meaningful for B_RGBA15 and B_RGBA32 bitmaps, as the alpha value is considered fully opaque for all other bitmaps. Conversely, this parameter is always considered to be B_CONSTANT_ALPHA for non-bitmap blitting, as there is no source pixel.

For example, if I have a B_RGBA32 bitmap of Jesse "The Body" Ventura, with the alpha at a constant 240 for the entire bitmap except for Jesse's dangerous, piercing eyes, which have alpha values of zero but are otherwise correct pixels, blitting Mr. Ventura with B_PIXEL_ALPHA will result in a slightly translucent Jesse with completely transparent holes where his eyes should be. However, blitting Jesse with B_CONSTANT_ALPHA and the high color alpha set to 128 will result in a half-transparent Jesse, with his eyes looking no more or less transparent than the rest of him.

The alpha function is trickier. I'll leave the full explanation for the Be Book, but the quick version is that there are two possible values, B_ALPHA_OVERLAY and B_ALPHA_COMPOSITE. B_ALPHA_OVERLAY is the one you're thinking of; it follows the conventions of the most popular OpenGL alpha blending mode. It should be used most of the time, and all of the time when blitting to the screen. B_ALPHA_COMPOSITE is slower, and is intended for compositing primitives onto a bitmap that has transparency which must be preserved. For instance, the translucent bitmap the R4 Tracker drags when moving or copying files is created using B_ALPHA_COMPOSITE. For the most part, if you need this, you know who you are. For more detailed information, check out the Be Book.

Asynchronous Controls

Part of the app server restructuring includes a new event model. In the past, we recommended polling the mouse with GetMouse() in the MouseDown() method of your views in order to track the mouse cursor or discover when a button is released. The reason was that there was no way to guarantee that the view that received a MouseDown() would receive the corresponding MouseUp(), or for that matter, intermediate MouseMoved() events for tracking purposes.

R4 makes GetMouse() polling more or less obsolete. Input and other types of app server events are now much smarter. They know how important they are (or aren't), and how to route themselves to whoever needs them. There is new API for telling them they're wanted:

status_t SetEventMask(uint32 mask, uint32 options=0);
status_t SetMouseEventMask(uint32 mask, uint32 options=0);

SetEventMask() is effective until you disable it by calling it again with mask set to zero. SetMouseEventMask() is effective until the next MouseUp(). The mask parameter for each call contains flags for the events you wish to receive. The only types right now are B_POINTER_EVENTS and B_KEYBOARD_EVENTS, which are fairly self-explanatory.

The "options" parameter provides extra information about how you want to route events. B_LOCK_WINDOW_FOCUS, which is only valid when using SetMouseEventMask(), prevents the window focus from changing, either from a focus-follows-mouse user moving the mouse outside the window or from another window requesting the focus, until the MouseUp(). B_SUSPEND_VIEW_FOCUS prevents B_KEYBOARD_EVENTS, which are normally dispatched to the window's focused view, from being sent there. This prevents, for instance, any typing you do while holding the mouse down on a checkbox from being sent to a text view of the same window that still has the focus. B_NO_POINTER_HISTORY tells the app server that you want only the most recent MouseMoved() events, preventing a backlog of events from queuing up for views that take a long time to process events.

As a result of these changes there is a new transit flag that can be passed to MouseMoved(): B_OUTSIDE_VIEW. This is for the case when the pointer is outside the view but the view has subscribed to pointer events. Also, MouseUp() events are now properly dispatched.

Also, some support methods have been added to the BControl class to make writing an asynchronous control easier and some API to BWindow to enable asynchronous Interface Kit controls in a given window. See the Be Book for details.

Background Bitmaps

One long-overdue feature in the Release 4 Tracker is the ability to place a bitmap on the Desktop. As most developers know, it's trivial to draw a bitmap into a view in the BeOS, so why did it take so long to implement desktop bitmaps? It's not because Pavel "Sizzlin'" Cisler, our Tracker engineer, is slow and has trouble finding his way to work in the morning. All those rumors are almost entirely unfounded.

The reason is that although it was possible to draw bitmaps, it wasn't possible to do it immediately. There was a delay before the update mechanism kicked in, and in the meantime the view filled with a background color. But if you drew a bitmap over this color, there was a great deal of flicker. Setting the view color to transparent helped, but there was still a delay, and on a loaded system there would be "window droppings" while the update waited to be scheduled. You may have seen a similar effect on Windows NT.

The solution we came up with is a general one, which provides for background bitmaps on any view, and gives tiling and positional options. The API is parallel to the SetViewColor() calls:

void SetViewBitmap(constBBitmap *bitmap,
                   BRect srcRect,
                   BRect dstRect,
                   uint32 followFlags = B_FOLLOW_TOP|B_FOLLOW_LEFT,
                   uint32 options = B_TILE_BITMAP);

void SetViewBitmap(constBBitmap *bitmap,
                   uint32 followFlags = B_FOLLOW_TOP|B_FOLLOW_LEFT,
                   uint32 options = B_TILE_BITMAP);

void ClearViewBitmap();

The window manager fills an exposed or invalidated view with the given bitmap at nearly the same time it would have filled it with the background color. The only time a delay occurs is when the bitmap is not in physical RAM and needs to be swapped in. The version without a source and destination rectangle assumes the upper-left corner at (0,0) and no scaling. ClearViewBitmap() removes any current view bitmap.

The follow flags work just as they do with views. The option flags right now are only for specifying tiling options. B_TILE_BITMAP, the default, tiles vertically and horizontally, while B_TILE_BITMAP_X and B_TILE_BITMAP_Y, astonishingly, tile either horizontally or vertically, respectively. For tiling cases, the destination rect you give is still obeyed, and the bitmap is tiled outward from there.

The bitmap you pass to these calls is referenced by the app server as the background for that view, but no copies are made. Therefore, it's legal and recommended to use the same bitmap for more than one view if the views in question have the same background. It's also legal to delete the bitmap while it's the background of one or more views. The app server keeps a reference count and clean ups the bitmap when all views using it either choose another background or are deleted.

This behavior means that changes you make to a bitmap after setting it as the background of a view are seen when the view is next refreshed and the bitmap is blitted to the screen.

New BShape API

The BShape class is more or less an encapsulation of a Postscript- style path. A BShape is composed of any number of connected or disjointed, open or closed polygons, described by line segments and quadratic Bezier curves. They can be stroked or filled in a BView, and, by drawing them into a BPicture and using ClipToPicture(), they can be clipped to.

void StrokeShape(BShape *shape,
                 pattern p = B_SOLID_HIGH);

void FillShape(BShape *shape,
               pattern p = B_SOLID_HIGH);

One difference with filled BShapes is that, unlike other filled primitives, they don't include edge pixels; that is, they do not assume the outside edge is stroked with a one-pixel-wide brush. This gives them some nice properties, including the ability to abut two BShapes, with no overlap or gap between them. I recommend using them whenever possible for high-precision or resolution-independent drawing.

BShapes also have accessor methods that let you read or modify points in a BShape. Modification can be done inline, since BShapes are completely client-side objects, allowing for easy translation or rotation of a shape. Because BShapes are composed entirely of lines and Beziers, they are invariable under all linear transformations. (You can perform nonlinear transformations, of course, but don't expect your shapes to look right afterwards!)

Exposed Font Glyph Information

In R4 it's possible to retrieve the shape of a particular font glyph from the system-wide font library. BShapes serve as the delivery mechanism for glyphs. The API is a new method of BFont:

void GetGlyphShapes(const char charArray[],
                    int32 numChars,
                    BShape *glyphShapeArray[]) const;

You allocate the BShapes, it does the rest. You can use the escapement information returned by other BFont methods to place your shapes for drawing. Because BShapes allow easy transformations, it's now possible to transform and distort text easily.

So Where's the Code?

I'd planned to write all this fancy sample code for y'all to demonstrate many of these new features, but...uh...I didn't. Too busy fixing bugs during R4 beta testing. I will, however, probably write up some sample code and put it into the Developer Library in a few weeks. Until then, it's up to you guys. I expect wonders! Get to work. Make Jesse proud.


Be Engineering Insights: Are You Experienced?

By Matt Bogosian

As the fog rolls over the mountains and threatens to envelop the Stanford Dish in a thick, cottony blanket, the setting sun breaks through for the first time on this brisk November day in Menlo Park, casting lukewarm light across the tree tops, and painting sharply contrasting shadows against the ubiquitous office-beige walls of my repo-depot erector set of a cube. I am a Be engineer.

So my day continues into the night in the countdown to Release 4. After spending my summers here as an intern, I've recently joined the Be team full time, with a mission to maintain what is known as "the Installation Experience." Welcome to the BeOS, may I take your order?

They (in the proverbial sense) say that one never gets a second chance to make a first impression. Just as your first handshake can tell the father of the girl you're dating that you're not a complete moron and are worthy of spending time in a well-lit, crowded place with his little princess, the installation process is frequently the first real exposure new users have to our product. That's why we'd like to make it as smooth and pleasurable as possible. This is what now rests on my shoulders.

There are two essential goals in writing almost any piece of software: simplicity and utility. In many cases "simple" is synonymous with "intuitive." I like to use a completely unscientific metric for quantifying simplicity in terms of the number of instructions read and number of mistakes made before one has a comfortable basic working knowledge of a given tool.

User manuals are rarely read. Therefore, it's important that a tool (however well-documented) have a base functionality and interface that allows users to "jump right in" and start being productive with a minimal amount of knowledge. This is easy if a program doesn't do much, but it becomes more difficult with a feature-rich everything-but- the-virtual-sink utility.

The trick is to present a tool's features in a logical, intuitive way. If a word processor requires users to learn a new scripting language -- documented only on an obscure overseas BBS whose number is listed on the CD-ROM on which the program is distributed—in order to change a font size, chances are that the interface could use some rethinking. The tool may be useful—it may let you capitalize every other letter of every third word in paragraphs beginning with the phrase, "It was the best of..."—but that functionality (however useful) will be hidden from most users if the interface is cryptic. Even expert users will be reluctant to use your features if they are inaccessible; intuitive interfaces are not just for neophytes.

In the case of the BeOS installer, these goals are two-fold. We'd not only like the BeOS to be easy to acquire, configure, and run, but we also want our developers to make a similar first impression. This is why I've undertaken the task of giving our installation utilities a major overhaul. My goal is to provide a more feature-rich set of tools that are easy to use for both developers and end users.

We've got the simplicity, now it's time to add some features. The current BeOS Installer is the result of efforts to maintain the ease of installing the OS. The notion of the "one button install" is a powerful one, and I believe it should be maintained. One badly needed feature that seems to be missing from the vocabulary of BeOS installers, however, is an "uninstall." Another welcome feature would be the ability to back out in the middle of an installation without affecting the volume onto which the package is being installed. From a developer's standpoint, I'd like to see more flexibility than just a "standard" install and an "optional" install. These are a few of many improvements that could be made.

I've just recently joined the team, so watch for an installation overhaul in Release 5. This gives you plenty of time to send in your feature requests (what do you want to install today?). Let me know if there's anything you can't live without or would find useful in a package creation or installation utility, and I'll worry about making it fun and accessible. You can e-mail your suggestions/comments/requests to <mattb@be.com>. As my Desktop background continually reminds me, I only have two more releases to buy one of these:

http://www.yamaha-motor.com/sport/products/modelimage/8/1/0/image.aspx
[Editors' note: Hopefully this is the correct URL as the page has moved between 1998 and 2006]

So I guess I'd better get back to work.


Developers' Workshop: Sounding Off With the New Media Kit

By Eric Shepherd

It was one year ago this week that I wrote my first Be Newsletter article, called "Sounds That Go Bump in the Night." In it, I explained how to play sound under the Preview Release 2 and Release 3 Media Kit. That was by subscribing to the audio stream, then filling buffers with 16-bit stereo audio data as it passes through your stream hook function.

Now we're on the brink of unleashing the new and improved BeOS Release 4 Media Kit, and it's a whole new ballgame. This article covers the new Media Kit's audio capabilities, with a special focus on updating existing applications to use the new API.

If you're not familiar with the Release 3 Media Kit, you don't really need to know how to update your applications, but if you're curious, you can read my two articles on sound playback in Release 3:

Developers' Workshop: Sounds That Go Bump In the Night

Developers' Workshop: Daddy, Where Does Sound Come From?

Sound playback in the Release 4 Media Kit is done with the BSound and BSoundPlayer classes. BSound represents a sound (either on disk or in memory), and BSoundPlayer provides functions for playing sound, either by playing a BSound, or by receiving buffers that you can fill in with audio data and pass along.

The former method is the easiest to use for new applications. Depending on the needs of your Release 3 Media Kit application, it may be easier to use BSound and BSoundPlayer together than the second method, which in many ways is similar to the previous Media Kit's stream hook function method for playing sounds.

Under Release 3, your sound playing code always involves a hook function that receives buffers from the Audio Server, fills them with data, and passes them along down the audio stream. This is usually overkill, especially if all you're doing is playing a sound from disk (see Developers' Workshop: Daddy, Where Does Sound Come From? for sample code on how this was done in Release 3).

Here's code that plays a sound from disk using the Release 4 Media Kit:

#include <MediaDefs.h>
#include <Sound.h>
#include <SoundPlayer.h>
#include <Entry.h>
#include <Path.h>

void playsound(char *path) {
  BSound *sound;
  BSoundPlayer player;
  entry_ref ref;
  BEntry entry(path, true);
  BSoundPlayer::play_id id;


  if (entry.InitCheck() == B_OK) {
    if (entry.GetRef(&ref) == B_OK) {
      sound = new BSound(&ref);
      if (sound->InitCheck() == B_OK) {
        player.Start();
        player.SetVolume(1.0);
        id = player.StartPlaying(sound);
        sound->ReleaseRef();
        player.WaitForSound(id);
      }
    }
  }
}

This function accepts a pathname as input, and plays the sound in the specified file. It doesn't return until playback is complete. Let's see how it works.

The most interesting local variables here are the BSound* and the id variable. BSound* is a pointer to the BSound object that represents the sound on disk, BSoundPlayer, which is the object that handles playing the sound. The id variable, of type BSoundPlayer::play_id, contains the ID number assigned to the sound by the BSoundPlayer when we start playing it.

The first thing it does is obtain an entry_ref for the path (using BEntry::GetRef()). Then a BSound object is instantiated, using that entry_ref. This sets up the BSound object to reference that file. There's also a form of the BSound constructor for creating sounds that are kept in memory.

Once the sound is instantiated, and we've checked to be sure it was done successfully, we start up the BSoundPlayer by calling Start(), set the volume to maximum by calling SetVolume(), and then start playing the sound using the StartPlaying() function.

The BSoundPlayer class can play as many sounds as you want (as long as every sound you play through a given BSoundPlayer is at the same sampling rate). That's why StartPlaying() returns an ID number. If you want to check the status of a sound, or stop it, you use that ID number to specify which sound you're interested in. For instance, you might want to call IsPlaying() to determine if the sound is still playing.

Once the sound has been started, we call BSound::ReleaseRef() on the sound to mark the sound to be deleted once playback is finished. That means that as soon as the BSoundPlayer is done with the sound, the BSound object is automatically deleted. Then we call BSoundPlayer::WaitForSound() to wait until the sound has finished playing.

If we wanted the playsound() function to be asynchronous, we would just remove the call to WaitForSound(), since we've arranged for BSounds to be deleted automatically when they're done playing.

That's the easy way to play sound in the R4 Media Kit. But what if your playback needs are more involved? Maybe your code needs to adjust the sound during playback, or your sounds are generated on the fly. The BSoundPlayer class can help you there, too.

BSoundPlayer supports the ability to specify a play buffer function that is called for every buffer that passes through the sound player's node. This lets you fill these buffers with whatever sound data you want. You create a BSoundPlayer for this purpose by either specifying the play buffer function when you instantiate the BSoundPlayer, or by calling BSoundPlayer::SetBufferPlayer() or BSoundPlayer::SetCallbacks(). You can also specify a cookie (application-specific data) that is passed to the play buffer function each time it's called.

In this example, we'll use stereo floating-point audio at 44.1kHz (this is the preferred format in the R4 Media Kit; floating-point audio is used internally, so by using it in your applications you can reduce overhead by preventing unnecessary format conversions). We'll create a play buffer function that plays a continuous triangle wave. The cookie tracks where we are in the wave across calls to the play buffer function. Here's the structure for the cookie:

typedef struct cookie_record {
  float value;
  float direction;
} cookie_record;

The main program instantiates the BSoundPlayer and handles running the sound. Since we're using the Media Kit's native sound format, we don't have to specify a media raw audio format structure to tell it what format to use. If we wanted to use, say, 16-bit stereo, we'd have to provide a media raw audio format structure to the BSoundPlayer constructor.

Keep in mind that Be reserves the right to change the default format. In fact, it's almost guaranteed to change over time as the Media Kit evolves, so you shouldn't assume that floating- point 44.1kHz stereo is the default. If you care, you should set it explicitly.

cookie_record cookie;
cookie.value = 0.0;
cookie.direction = 1.0;
BSoundPlayer player("wave player", BufferProc, NULL,
  &cookie);

We now have a BSoundPlayer that's tied to a play buffer function called BufferProc(). It receives a pointer to our cookie each time it's called.

Here's some simple code that starts and stops the tone:

player.Start();
player.SetHasData(true);
getchar();      /* wait until return is hit */
player.Stop();

The BBufferPlayer::Start() and BBufferPlayer::Stop() functions start and stop the playback, as in the previous example. The SetHasData() function is new: it tells the BSoundPlayer that our play buffer function is in fact ready to generate audio data. Your play buffer function won't be called until this has been done. When you don't have sound to play, you should call SetHasData(false) so the Media Kit doesn't keep calling your play buffer function when there's no sound to play; this improves performance.

Now let's have a look at the BufferProc():

void BufferProc(void *theCookie, void *buffer, size_t size,
       constmedia_raw_audio_format &format) {
  size_t i, j;
  float *buf = (float *) buffer;
  size_t float_size = size/4;
  uint32 channel_count = format.channel_count;
  cookie_record *cookie = (cookie_record *) theCookie;


  if (format.format !=
        media_raw_audio_format::B_AUDIO_FLOAT) {
    return;
  }


  for (i=0; i<float_size; i+=channel_count) {
    for (j=0; j<channel_count; j++) {
      buf[i+j] = cookie->value;
    }
    if ((cookie->direction == 1.0) &&
        (cookie->value >= 1.0)) {
      cookie->direction = -1.0;
    }
    else if ((cookie->direction == -1.0) &&
             (cookie->value <= -1.0)) {
      cookie->direction = 1.0;
    }
    cookie->value += cookie->direction*(1.0/64.0);
  }
}

This function (if you're familiar with the Release 3 Media Kit) is the R4 equivalent of the enter stream hook function used when you subscribe to a BDACStream to pump audio buffers into the stream. The key difference is that in this case, the buffers are yours and yours alone; you should replace their contents with your own data. In Release 3, you received buffers containing data provided by other applications that were playing sounds.

The code begins by setting up some local variables. A local pointer to the audio buffer is created, of type float*, since that's the format of the data we'll store in the buffer. We compute float size, the number of floats in the buffer, and we fetch the channel count out of the media raw audio format structure we receive that describes the audio format. We also set up a pointer to the cookie of the correct type.

Then we check to be sure that the buffer's audio format is in fact floating-point by comparing its format to the value media_raw_audio_format::B_AUDIO_FORMAT. If it doesn't match, we return without doing anything.

Then we enter a loop that fills out the buffer with data. How this works isn't really important. You can replace this code with whatever you want in order to provide the sound you require. The only interesting thing to note is that parameters for building the audio are pulled from the cookie, and updated values are stored back into its fields before the BufferProc() returns.

If your Release 3 Media Kit application simply plays sound, you should be able to use one of these two methods to update your R4 Media Kit application easily. If your code is straightforward, and plays sounds unaltered, you may find it easier to use the BSound and BSoundPlayer combination to perform the audio. If your enter stream hook function is more complex, performing audio generation on the fly, the play buffer mechanism should let you update your application without much effort.

About the only applications that can't readily be updated to the R4 Media Kit by using these two classes are those that filter the audio output by other applications, or applications that record audio. For these, you'll have to look deeper into the Media Kit.


More Notes From the Road: BeOS in Tokyo

By Jean-Louis Gassée

As I write this, we're in a conference room in Hitachi's Personal Computer Division headquarters, near Shin Ochanomizu in Tokyo, just steps away from the geek Mecca of Akihabara. We're working on the last details of presentations to introduce three Hitachi Flora Prius systems bundled with Gobe Productive and Adamation Personal Studio applications for the BeOS, in a partition next to the standard Windows 98 binaries.

This event is a little more complicated than usual, but it's a happy one nonetheless. Some time ago, we made a commitment to the Japanese market, based on reasons and emotions I've discussed in prior columns. Japan is, after all, the birthplace of much digital media technology. It is a market of technophiles and, in contradiction to the conservative stereotype affixed to its culture, fairly comfortable with unproven ideas.

It also didn't hurt that a number of us at Be are unabashed Japanophiles. Singling out anyone's contribution in integrated work such as ours could be unfair to the rest of the team, but today's occasion reminds me that we wouldn't be here without one of our engineers, Hiroshi Lockheimer, who made sure we have a truly Japanese version of the BeOS.

From where I sit, I can see Bob Hearn, Gobe's CTO, entering Japanese text in a Productive document for his demo. One of the complications mentioned earlier involved flying Bob out of Hawaii, hours after his sister-in-law's wedding. Stephan Adams also made the trip to introduce Personal Studio. All of us, including Jim Cook, Michael McBride, and myself, along with spare equipment and software in a broken case, a translator and a number of our Hitachi hosts, are now rehearsing a four company presentation in two languages (and more accents). Our audience is dailies in the morning and magazines in the afternoon.

The good news is that Hitachi PR people are running around looking for a bigger room—it looks like we'll have more media people than originally planned. Or else our Hitachi friends know the rule regarding the number of seats in the room before a presentation: stack half the chairs in a corner and make a spectacle of putting them back when the available ones are filled. That way the presentation starts with an audience that thinks it's larger than planned. Or, if only half the people you expected show up, you don't start with a group of people wondering why the other half haven't bothered to attend.

The preparation is meticulous and high tech. Hitachi requests a change in my presentation, I scribble it on a piece of paper; Morita-san, the translator, edits a slide on his PowerBook, and e-mails the changes. The new color-printed hand-outs are produced somewhere else in Tokyo. I notice he's using a tiny Kenwood wireless phone—at ISDN speed, I'm told—from the inside of a reinforced concrete building. Unlike a dedicated modem such as the Ricochet, this uses a standard digital phone. It appears faster, more reliable, and more common than what we enjoy today in the US or in Europe.

Hitachi and our Japanese distributor, Plat'Home, have supported the BeOS from the beginning of its market life. Month after month, Plat'Home has generated a disproportionately high percentage of our sales volume. They also introduced us to Hitachi, leading to the present joint effort. Interestingly, after taking a long look at the BeOS and at the marketplace, Hitachi came to its own conclusions—remarkably similar to ours—regarding what they call "media creation and browsing," the respective capabilities of Windows 98 and of the BeOS, and their coexistence.

During Hitachi's pre-introduction briefings to selected media, one question was asked repeatedly: Why does Hitachi, a very conservative company, make such an unconventional move? This is more the style of Sony.

Somehow, this seemed to make Hitachi executives happy.

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.