If I were in Marketing, I would come up with a tag line for BeDepot.com that went something like this: "Using the BeOS isn't hard, shopping for it shouldn't be either." As David Byrne said in True Stories, "Shopping is a feeling."
Unfortunately, of late, the BeDepot.com that we have all come to know and love has been incredibly difficult to use, and the prevailing feeling for users, developers, and the BeDepot.com team alike has been one of frustration.
How did this happen? Putting it succinctly, BeDepot.com was conceived as a simple but elegant solution for web commerce in an era that pre-dated the distribution of physical software, a break in the binary compatibility of the BeOS, and the idea that developers would want to distribute binaries for PPC and Intel separately. We have augmented the system to handle all of these tasks, but not quite seamlessly. This, coupled with a few select design problems inherent in SoftwareValet has caused users some grief.
The good news is that we are working to put real solutions in place that will make shopping at BeDepot.com a pleasure once more. Most notably, we will be introducing "WebValet" which gives users online access to all available updates for the software you've bought through BeDepot.com. In the mean time, we have come up with the following workarounds to the more common problems that users are encountering.
As people move from R3 to R4, a common problem is that they inadvertently overwrite their SoftwareValet settings as they do a clean install. SoftwareValet settings are generated whenever you install a PackageBuilder package. They are generic files that BeDepot.com uses to update your software. Without these settings, you can't update.
- Workaround 1: If you back up your R3 SoftwareValet settings, you can install them in your R4 settings directory and update as normal under R4. See the SoftwareValet documentation for more into on how to back up these settings.
- Workaround 2: If you've already lost your SoftwareValet settings, you can write to [support@bedepot.com] and we can regenerate your settings for you.
Some software on BeDepot.com is available for both R3 and R4. Some software is only available for one or the other. When you order or download software, it is labeled as either R3 or R4 software. Be sure to select the appropriate version for your machine.
- Workaround: If you download the wrong version of an application, if there exists a version for your machine, BeDepot.com can get it to you. If the correct version does not exist for your current version of the BeOS, BeDepot.com will gladly refund your money. Write to [support@bedepot.com]. Common Update Problems
Because of the design of the Newer Versions window in the SoftwareValet SoftwareManager, only one available version shows up at a time in the window. As a result, it is very easy to download the wrong version. Unfortunately, due to a bug in SoftwareValet, you can only update once. Which means that if you get the wrong version the first time, you're stuck.
- Workaround: BeDepot.com can arrange for you to get the update you need. Write to [support@bedepot.com].
Many of the products offered through BeDepot.com offer "platform switching." That is, if you buy the Intel version, you get the PPC version for free or vice versa. However, in practice, if the program is split into separate binaries for each platform, you can only access the one you bought.
- Workaround: Currently you can only download the version that you bought. However, you can obtain the other platform version through BeDepot.com directly by writing to [support@bedepot.com].
From time to time, BeDepot.com users encounter ODBC errors. These are often due to timeouts and seem to occur most often during peak hours. However, on occasion, these are warnings that you have encountered a bug in the system.
- Workaround: If you encounter an ODBC error, and it looks like a timeout, chances are good that the server is just busy. Wait for a while and try again later. If the ODBC error you get looks like it is something other than a timeout, feel free to copy the error message and send it to us, so we can identify and fix the problem. You can send bug reports to Customer Support at: [support@bedepot.com].
You ordered your update a while ago but you still haven't received a copy. You're sure that you've registered your copy of the BeOS on the Be web site, too.
- Workaround: If you have registered your copy of the BeOS and you still haven't received your copy of R4, you can look up the status of your order on the BeDepot.com web site in the Your Account area, under Order Status. If your order is listed as "On Hold," chances are good that your order is on hold while we look up your registration and confirm it. If your order has been on hold for more than two weeks, please notify Customer Service at: [custservice@bedepot.com]. For Further Information
In addition to this list, good resources for BeDepot.com information include the BeDepot.com Help Area [http://www.bedepot.com/Support/index.html] and the SoftwareValet FAQs.
When you need to contact a human being, we've got that covered, too. For ordering issues, you can e-mail [custservice@bedepot.com]. For technical support issues regarding BeDepot.com, you can e-mail [support@bedepot.com]. For technical issues regarding the BeOS proper, please e-mail [support@be.com].
Here's an idea we've wanted to try out for a long time. It's always amazing how many cool color pickers there are for BeOS: roColor, for a start, and more in Gobe Productive, ArtPaint, new Mail-It, Pe, and other applications. What if you could choose the coolest one and hook it up as the preferred color picker for all the apps on your disk? But because different apps may need different color pickers, it would be best to have both a default picker and application specific pickers. If color pickers are written as a separate module, we should be able to choose the preferred application for color selection in FileTypes, the same way we can select a preferred application for text/source-code. Here's how it could work.
A color-defining label/view interacts with a color picker module using a messaging suite, designed for selecting a color. The messaging suite is well defined, in the same way that something like the format of an RTF document is. The suite has a MIME type associated with it, for example, application/x-vnd.Be.colorPicker. When a color label is invoked, the preferred application for application/x-vnd.Be.colorPicker is launched and a messaging protocol is established.
For this to work right, we need to integrate the color picker application and the color label that launches it really well. Ideally it should seem as if the module, a separate application, were really just a color picker dialog in the same application.
The messaging suite will let us do the following:
Launch the color picker application and establish a connection.
Pass in an initial color value.
Pass in the name of the target color label that the color picker can use to identify its target (in the window title, for example).
Pass in the initial click location to allow positioning of the color picker.
Send color updates from the color picker module to the client color label.
Send color updates back in the other direction—if the color label is updated, say by drag and drop, the color picker has to catch up and send Apply, Cancel, and possibly Revert messages back from the color picker to the color label.
Break down a connection when the user closes the color picker.
Quit the color picker when the invoking color label goes away.
If we can handle all of actions, the color picker should be pretty well integrated.
Included with this article are three small apps that demonstrate how to implement this scheme. One is a simple application with two color labels you can use to set color values in preference panels (for example). The other two are different color picker panels.
You'll find the source code for this article at
[ftp://ftp.be.com/pub/samples/application_kit/colorPickerModule.zip]
The code contains most of the fun stuff in this article—we'll just point out some of the important parts here.
The first color picker is very limited; it just uses the built-in
BColorControl
. The second one is less trivial; it implements a
BColorControl
-like crayon color picker.
The sample application uses a ModuleProxy
class to
manage the connection to the color picker module. This is done more for
tidiness than anything else: ModuleProxy
makes it
easy to hook up the described protocol into color labels like
ColorLabel
. It's hard-coded to deal with
rgb_color values but could be easily modified to accommodate
different value types by turning it into a template class.
The destructor of ModuleProxy
sends a message to any open color picker to
quit itself, so we don't get color pickers hanging around when the
originating dialog is gone. The UpdateValue call is used when the target
client—the ColorLabel
—changes its value without the color picker
knowing. This way the color picker can follow the value by updating
itself.
MessageReceived()
handles messages from the color
picker—forcing an update of the ColorLabel
when
the color picker changes—and establishes and tears down the connection
with the color picker. Invoke()
is called from the
ColorLabel
. Invoke()
is a
little more interesting: if a color picker isn't running yet, it launches
one, sending it a setup message.
voidModuleProxy
::Invoke
() { if (connectionOpen
) {module
.SendMessage
(B_WINDOW_ACTIVATED
); // we already have a picker serving us, pull it up return; } ... uint32buttons
;BPoint
point
;target
->GetMouse
(&point
, &buttons
);BMessage
launchMessage
(kInitiateConnection
);launchMessage
.AddMessenger
(kClientAddress
,BMessenger
(this
)); // this is the messenger we want the color picker to // interact withlaunchMessage
.AddPoint
(kInvokePoint
,target
->ConvertToScreen
(point
)); // add the current invocation point so that the color // picker can position itself near the clicklaunchMessage
.AddString
(kTargetName
,target
->Name
()); // add the current invocation point so that the color // picker can position itself near the click rgb_colorcolor
=target
->ValueAsColor
();launchMessage
.AddData
(kInitialValue
,B_RGB_COLOR_TYPE
, &color
,sizeof
(color
)); // add the current color valuelaunchMessage
.AddInt32
(kRequestedValues
,B_RGB_COLOR_TYPE
); // ask for the type of values we need if (preferredApp
.Length
()) // we have a specific preferred application for this // instance launch the picker - use the application // signature for this particular clientbe_roster
->Launch
(preferredApp
.String
(), &launchMessage
); elsebe_roster
->Launch
(type
.String
(), &launchMessage
); // launch the picker, just use the mime type // to choose the preferred application }
Note that Invoke uses the
BRoster::Launch
(const char* mimeType
,
BMessage
*,...)
call to start up the color picker. If a signature for the preferred color
picker was specified, it will be launched; otherwise it uses the default
preferred color picker for the MIME type. It's similar
to what happens when you double-click a document: it may have a specific
preferred application signature or it may just use the preferred
application for the document's type.
launchMessage
goes to the color picker
application's MessageReceived()
:
voidSimplePickerApp
::MessageReceived
(BMessage
*message
) { if (message
->what ==kInitiateConnection
) { // This is the initial open message that // ModuleProxy::Invoke is sending us. Pass it on // to the new color picker dialog which will // find all the details in itcolorPicker
= newSimpleColorPickerDialog
(message
);colorPicker
->Show
(); return; }BApplication
::MessageReceived
(message
); }
SimpleColorPickerDialog
extracts the information about the dialog
position, initial color, window title, etc., and replies back to the
ModuleProxy
, completing the setup.
The destructor posts a message to the client application that the color
picker was closed, so if the user clicks on the color label, ModuleProxy
can launch a new copy.
MessageReceived()
handles the rest of the
protocol, closing the connection, responding to
B_VALUE_CHANGED
, and posting the appropriate messages
for Cancel and OK.
When you right-click on the color label, you get a pop-up menu that allows you to select the preferred color picker application for the label. The code is similar to the one used by FileTypes to select a preferred application for a MIME type:
voidModuleProxy
::RunPreferredPickerSelector
(BPoint
where
) {BPopUpMenu
*menu
= newBPopUpMenu
("preferredApp");BMenuItem
*item
= newBMenuItem
("Default", 0);menu
->AddItem
(item
);menu
->AddSeparatorItem
();BMimeType
mime
(type
.String
()); // build a list of all the supporting appsBMessage
message
;mime
.GetSupportingApps
(&message
); for (int32index
=0; ;index
++) { const char *signature
; status_treply
=message
.FindString
("applications",index
, &signature
); if (reply
!=B_NO_ERROR
|| !signature
|| !signature
[0]) break;BMessage
*tmp
= newBMessage
;tmp
->AddString
("signature",signature
); entry_ref entry; if (be_roster
->FindApp
(signature
, &entry
) ==B_OK
) // add the application by its nameitem
= newBMenuItem
(entry
.name
,tmp
); else // can't find the app, just use the signatureitem
= newBMenuItem
(signature
,tmp
);menu
->AddItem
(item
); // mark the preferred app if (preferredApp
.ICompare
(signature
) == 0)item
->SetMarked
(true
); } if (!menu
->FindMarked
()) // mark "Default"menu
->ItemAt
(0)->SetMarked
(true
); // make the selected signature preferreditem
=menu
->Go
(where
); const char *signature
; if (item
) { if (!item
->Message
()) // picked "Default"preferredApp
= ""; else if (item
->Message
()->FindString
("signature", &signature
) ==B_OK
)preferredApp
=signature
; } deletemenu
; }
If you compile the application and the two color pickers, note that when you click on a color label, it launches a color picker application; the title of the window is identical to the name of the color label; and the initial color of the color picker matches the color label. You can right-click on the color label and choose the preferred color picker for the given label. If you keep the default setting, you can use FileTypes to pick which of the two color pickers will be used. If you change the color in the picker, it's updated in the color label. If you drag and drop a swatch between the two color labels (click on one of the color labels and start dragging) to change the label color, the color picker will follow. If you drag and drop a color from roColor, it changes to that color. If you quit the application, the color picker quits too -- which is just what we wanted it to do!
Some possible enhancements—fun things to try:
Allow multiple connections—not very practical in this case, but each time you Invoke the color label, you can instantiate a new ModuleProxy, adding them to a list, each time a color picker quits you delete it. You could have two color pickers target the same color label; with a little bit of tweaking you could have it set up so that changing one color picker not only updates the color label, but also the second color picker.
Use scripting instead of a rigid messaging protocol. The color values can be passed back and forth between the color label and the color picker using the Get/Set property calls.
Write a modular volume control, etc. You could apply this technique to writing other things than just color pickers.
Several weeks ago, Sheppy gave us a starting point for learning how to use the Media Kit:
Developers' Workshop: Sounding Off With the New Media Kit
For many of you, however, that's not nearly enough. You want to see the
creatures that lurk under BSoundPlayer
's tame surface. You want to blast
buffers over your media net like terrified badminton shuttlecocks. You
want to run your fingers through the fertile Media Kit soil. And you need
the whole system at your fingertips, yesterday.
Suits me fine. Here's some code for you to peruse:
[ftp://ftp.be.com/pub/samples/media_kit/SoundCapture.zip]
This app does two things. First, it records sounds, using an optional voice-activation feature, and saves the sounds as raw audio files. Second, it loads and plays back raw audio sounds so you can audition what you recorded.
Straightforward this app is, but simple it ain't. It would take me much longer than a Newsletter article to explain its operation from the ground up. So, before you read on, please familiarize yourself with the basic concepts of the Media Kit by browsing the freshly painted Be Book:
For now, you'll want to concentrate on the Media Kit classes BMediaNode
,
BBufferConsumer
, BBufferProducer
,
and BMediaRoster
. You might also glance
at the header file MediaDefs.h
,
which describes many of the lower-level
structures and defines that we use. Then, look at this article and the
sample code to see how it all fits together!
The code is pretty heavily commented, even for a DTS project, so I'll spare the details and just touch on a few of the more important issues here. OK—let's get down and dirty with the Media Kit...
There are three key classes that make SoundCapture tick: SoundConsumer
,
SoundProducer
, and CaptureWindow
.
SoundConsumer
is a franchise of
BBufferConsumer
; its job is to process (i.e.,
"consume") buffers that are sent to it from a higher source.
We've tried to make this class reusable by making it reasonably extensible.
In a similar manner to BSoundPlayer
, we do this by
defining two places where you, the SoundConsumer
client, need to decide what to do:
The Process()
function, which is called each time a buffer of data
arrives. You provide functionality to tell it what to do with these
buffers.
The Notify()
function, which is called each time something important
happens to the node (it starts, stops, or dies an abrupt death, for
example). You provide functionality to dispatch any of these events as
you see fit.
You fill in these slots by either subclassing SoundConsumer
and
overriding the function, or simply by passing SoundConsumer
a hook
function to call instead.
We use the SoundConsumer
as a simple recorder.
We implement a Process()
hook function that writes the buffer's data to disk, and a Notify()
hook
function to makes sure that things get cleaned up when the node's about
to stop or die.
SoundProducer
is a subsidiary of
BBufferProducer
; its job is to produce buffers like
clockwork and pass them along to someone else. Like
SoundConsumer
, we provide two places for you to
specialize SoundProducer
's behavior:
Process()
, which is called each time a buffer
needs to be filled with data; and Notify()
, which
is called when certain events happen.
We use the SoundProducer
to implement simple
playback from a sound file. In this sense, it functions almost exactly like
BSoundPlayer
and BSound
(save
that it only reads raw audio, doesn't handle multiple sounds, and doesn't
do sample format conversion).
In addition to fulfilling its traditional duties as a
BWindow
, CaptureWindow
also
functions in SoundCapture as the context in
which SoundConsumer
and
SoundProducer
are used.
CaptureWindow
contains an instance of each class,
and does the following:
When you click on the CaptureWindow
connects its
SoundConsumer
to the system's audio input and starts the node.
Similarly, when you click on the CaptureWindow
connects
its SoundProducer
to one of the system's mixer inputs and starts the
node.
Finally, when you click on Process()
or Notify()
hook functions has determined that the node is done recording or
playing), CaptureWindow
figures out which node is running, stops the
node, and disconnects it.
Looking at the responsibilities of a BBufferConsumer
and a
BBufferProducer
, you'll notice that there are a lot of similarities. Here
some of the more important ones:
Both consumers and producers create a port, called the Control Port, that the Media Server uses to send messages to them. Messages range from performance event (Start, Stop, and Seek) requests, to receiving buffers, to messages that you define and send yourself.
Both create a thread, lovingly referred to as the Big Bad Service Thread, which is responsible for handling messages sent to the Control Port in a timely manner. Among the other things that this thread might do, its primary responsibility is to read from the Control Port until a message is received, and then handle the message.
Both override BMediaNode
functions that implement certain important
performance events. Start tells you when your node needs to start. Stop
tells you when your node needs to stop. Finally, Seek tells you when
you need to change your media time, and what the new media time should
be. (More on this in a bit.)
At the same time, there are several significant differences that you should be aware of. Here's a blow-by-blow:
Your node can interpret Start, Stop, and Seek events in various ways, depending on what makes sense for your node.
For SoundProducer
, Start and Stop are interpreted to mean "start
producing buffers" and "stop producing buffers." Our application does not
currently define the concept of media time for its sound producer, so
SoundProducer::Seek has no effect. In the future, media time would
probably be interpreted as the offset in the sound file you're playing.
On the other hand, SoundConsumer
currently defines no behavior for Start
and Stop, and simply accepts buffers at any time. It also doesn't do
anything meaningful with Seek requests, but passes the media time as a
timestamp to its Process()
hook function, in case that time has any meaning
to SoundConsumer
's client.
As the BeOS media system generally runs in real time, timing issues are perhaps the most critical part of developing a media node, and are often the trickiest part to get right.
SoundConsumer
has it easy, since all it needs to do is grab buffers as
they arrive and blast them to disk. It doesn't even care whether the
buffers were on time or not. So, all it needs to do is sit around in its
Big Bad Service Thread, waiting patiently for those buffers to arrive.
Once messages do arrive, it handles them immediately.
Because it doesn't really care about Start, Stop, or Seek requests,
SoundConsumer
"handles" them instantaneously, instead of queuing them up
for handling at a particular time. More complicated consumers might need
to handle performance events accurately, and might need to determine
whether incoming buffers are running behind.
SoundProducer
, on the other hand, is a lot more complicated. Not only
does it need to handle performance events at their correct times, but it
also needs to produce a steady stream of buffers—and those buffers
have to be sent at precisely the right time, so that they don't reach the
final output (your headphones) too late, or so early that the latency of
the system is more than it needs to be. Its Big Bad Service Thread,
therefore, does three different things:
It checks to see if any pending performance events need to be handled, and handles them when they do.
It checks to see if any messages have arrived at the Control Port, and handles them when they do.
When it's time to produce a buffer, SoundProducer
stuffs a buffer
(using the Process()
function) and sends it off.
One of the keys to understanding the timing of SoundProducer
is the
timeout value passed to read_port_etc()
. This value determines how long the
thread waits in each iteration of the loop for messages to arrive. This
timeout is set to the time until the next performance event needs to be
handled, or until the next buffer needs to be produced, whichever comes
first. So, this call to read_port_etc really serves the dual purpose of
receiving messages and snoozing until the next thing needs to happen!
The other key to understanding SoundProducer
timing is the value returned
by BTimeSource::RealTimeFor()
. This somewhat misnamed function does not
give the absolute real time that corresponds to a given performance time,
but rather gives you the real time that you need to do things in order
for their effects to take place at the performance time. It does this by
taking into account the latency that you give it—that is to say, you
tell it the difference between the time at which you decide to do
something, and the time at which that something actually takes effect.
The greater your latency, the earlier you must start things for them to
happen on time. And, as I have been reminded on any number of occasions,
being on time is extremely important.
There are several directions in which this app can grow. In particular,
you could extend either SoundConsumer
or
SoundProducer
to do all sorts of
wild stuff. You could override the consumer's hook functions to provide,
for example, an oscilloscope node, or a node that performs spectral
analysis. You could also override the producer's hook functions to
perform sound synthesis.
Go nuts!
If I remember correctly, one difference between organic and mineral chemistry is the introduction of time as a variable in the reaction. In the mineral world, the ingredients meet and react; the result is a fixed one, dependent only upon the initial conditions. Organic reactions, on the other hand, introduce another variable—time. These reactions are slower, and the result is a function of the time of measurement.
It's the organic model that comes to mind when I look at the pace of acceptance of new standards. Some organic reactions actually never take off; for example, ISDN, a high-speed local loop in every pot. The appearance of others might be a little misleading—here I'm thinking of the Internet, which might cause problems with placing the time origin of the reaction. What we observe is the fast middle branch of the S-curve. We might forget the two decades of slow gestation of the first branch in government and university research labs.
With a new standard, we often have the misleading intuition of a quick reaction, only to find that the actual pace of adoption is more organic than we'd like. USB appears to be one such example. On the "obvious" side, it's got to happen—because it's good. Higher throughput; peace with interrupts; self-identifying peripherals; hubs; smaller, nicer connectors; cheaper; saves trouble; better ergonomics.
USB is promoted by Intel, supported by Microsoft, and has recently been discovered by Apple. On the Intel promotion side, the effort started five years ago, if memory serves; motherboards with USB support appeared more than two years ago. Software support lagged but became somewhat real (we'll get to the "somewhat" in a moment) with Windows 95 OSR 2.1. OSR stands for OEM Service Release—midstream fixes and improvements distributed to OEMs. But peripherals manufacturers had been burned before, and didn't jump to support USB either.
Now we have Windows 98 with USB support and motherboards with USB hardware, but very few USB peripherals such as printers and modems. There are exceptions, such as a Kodak digital camera, but more than two years after the appearance of USB connectors on the back of PCs, that's all we have—exceptions. Or, take the surreal case of the new Macs. Much has been made of the iMac's lack of floppies; to use them you have to buy a USB external Superdrive. It's nice, color coordinated (before the new colors were offered), but a tad expensive at $149, plus almost $9 for a 120 MB floppy.
One can argue you get the best of both worlds—a drive that uses old and new floppies (I think) and offers the capacity of a Zip drive. But turn your attention to the new, colorful high-end G3 Macs. They don't come with a Superdrive or a floppy, though some have a Zip drive. Here the surreal part is the modem. The internal modem is only available on configurations ordered from Apple, I'm told. You can't get a USB modem, the internal modem isn't available as a part, and there are no drivers for "older" modems connected via a USB-to-serial adapter.
When I asked a leading Mac retailer how they felt about this, I got what the Car Guys call the mechanic's shrug. Hopefully, this will become an anecdote as USB peripherals and software become available. We, of course, will do our part Real Soon. But now we wonder about FireWire—will it take as much time as USB to become more than "somewhat real," or will recent efforts by Apple to assert IP rights to the standard slow its organic growth?