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.
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.
BView
s, in all previous releases, have been too heavyweight for many
applications. A major valid complaint has been that BView
s 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.
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.
R4 introduces the long-awaited ability to clip to an arbitrary shape,
rather than the resolution-dependent, awkward
BRegion
s. 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.
voidClipToPicture
(BPicture
*picture
,BPoint
where
=B_ORIGIN
, boolsync
=true
); voidClipToInversePicture
(BPicture
*picture
,BPoint
where
=B_ORIGIN
, boolsync
=true
);
There are many advantages to using this new clipping API.
BPicture
s 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
BPicture
s 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.
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:
voidSetBlendingMode
(source_alphasrcAlpha
, alpha_functionalphaFunc
);
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.
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_tSetEventMask
(uint32mask
, uint32options
=0); status_tSetMouseEventMask
(uint32mask
, uint32options
=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.
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:
voidSetViewBitmap
(constBBitmap
*bitmap
,BRect
srcRect
,BRect
dstRect
, uint32followFlags
=B_FOLLOW_TOP
|B_FOLLOW_LEFT
, uint32options
=B_TILE_BITMAP
); voidSetViewBitmap
(constBBitmap
*bitmap
, uint32followFlags
=B_FOLLOW_TOP
|B_FOLLOW_LEFT
, uint32options
=B_TILE_BITMAP
); voidClearViewBitmap
();
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.
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.
voidStrokeShape
(BShape
*shape
, patternp
=B_SOLID_HIGH
); voidFillShape
(BShape
*shape
, patternp
=B_SOLID_HIGH
);
One difference with filled BShape
s 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
BShape
s, with no overlap or gap between them. I
recommend using them whenever possible for high-precision or
resolution-independent drawing.
BShape
s also have accessor methods that let you read
or modify points in a BShape
. Modification can be
done inline, since BShape
s are completely
client-side objects, allowing for easy translation or rotation of a shape.
Because BShape
s 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!)
In R4 it's possible to retrieve the shape of a particular font glyph from
the system-wide font library. BShape
s serve as the delivery mechanism for
glyphs. The API is a new method of BFont
:
voidGetGlyphShapes
(const charcharArray
[], int32numChars
,BShape
*glyphShapeArray
[]) const;
You allocate the BShape
s, it does the rest. You can use the escapement
information returned by other BFont
methods to place your shapes for
drawing. Because BShape
s allow easy transformations, it's now possible to
transform and distort text easily.
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.
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.
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> voidplaysound
(char *path
) {BSound
*sound
;BSoundPlayer
player
; entry_refref
;BEntry
entry
(path
,true
);BSoundPlayer
::play_idid
; if (entry
.InitCheck
() ==B_OK
) { if (entry
.GetRef
(&ref
) ==B_OK
) {sound
= newBSound
(&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
BSound
s 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_recordcookie
;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
so the Media Kit doesn't keep calling your
play buffer function when there's no sound to play; this improves
performance.
SetHasData
(false
)
Now let's have a look at the BufferProc()
:
voidBufferProc
(void *theCookie
, void *buffer
, size_tsize
, constmedia_raw_audio_format &format
) { size_ti
,j
; float *buf
= (float *)buffer
; size_tfloat_size
=size
/4; uint32channel_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.
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.