Issue 4-3, January 20, 1999

BEDEPOT.COM: Shopping is a Feeling

By Melanie Walker

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.

Common Problems Moving from R3 to R4

Problem: Lost SoftwareValet settings.

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.

Problem: Ordered/downloaded software for the wrong version of the BeOS.

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

Problem: Downloaded wrong update / scrambled update.

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].

Problem: Updating through another browser.

My BeOS partition isn't configured for the Internet. Can I update through another web browser?

- Workaround: Right now, the only way to update is through SoftwareValet and therefore you must be using the BeOS.

General BeDepot.com Problems

Problem: Trouble accessing the other version of a product with platform switching.

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].

Problem: ODBC Errors.

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].

Problem: R4 Update Delays.

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].


Be Engineering Insights: Writing a Modular Color Picker

By Pavel Cisler, Robert Chinn

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:

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.

void
ModuleProxy::Invoke()
{
  if (connectionOpen) {
    module.SendMessage(B_WINDOW_ACTIVATED);
    // we already have a picker serving us, pull it up
    return;
  }
  ...

  uint32 buttons;
  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 with
  launchMessage.AddPoint(kInvokePoint,
    target->ConvertToScreen(point));
    // add the current invocation point so that the color
    // picker can position itself near the click
  launchMessage.AddString(kTargetName, target->Name());
    // add the current invocation point so that the color
    // picker can position itself near the click

  rgb_color color = target->ValueAsColor();
  launchMessage.AddData(kInitialValue, B_RGB_COLOR_TYPE,
    &color, sizeof(color));
    // add the current color value

  launchMessage.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 client
    be_roster->Launch(preferredApp.String(), &launchMessage);
  else
    be_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():

void
SimplePickerApp::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 it
    colorPicker = new SimpleColorPickerDialog(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:

void
ModuleProxy::RunPreferredPickerSelector(BPoint where)
{
  BPopUpMenu *menu = new BPopUpMenu("preferredApp");
  BMenuItem *item = new BMenuItem("Default", 0);
  menu->AddItem(item);
  menu->AddSeparatorItem();

  BMimeType mime(type.String());

  // build a list of all the supporting apps
  BMessage message;
  mime.GetSupportingApps(&message);
  for (int32 index =0; ; index++) {
    const char *signature;
    status_t reply = message.FindString("applications",
      index, &signature);

    if (reply != B_NO_ERROR || !signature || !signature[0])
      break;

    BMessage *tmp = new BMessage;
    tmp->AddString("signature", signature);

    entry_ref entry;
    if (be_roster->FindApp(signature, &entry) == B_OK)
      // add the application by its name
      item = new BMenuItem(entry.name, tmp);
    else
      // can't find the app, just use the signature
      item = new BMenuItem(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 preferred
  item = 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;
  }

  delete menu;
}

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:


Developers' Workshop: Media Kit Basics: Consumers and Producers

By Owen Smith

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:

index.html

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...

Under the Hood

There are three key classes that make SoundCapture tick: SoundConsumer, SoundProducer, and CaptureWindow.

SoundConsumer

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

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).

CaptureWindow

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 Record button, CaptureWindow connects its SoundConsumer to the system's audio input and starts the node.

  • Similarly, when you click on the Play button, CaptureWindow connects its SoundProducer to one of the system's mixer inputs and starts the node.

  • Finally, when you click on Stop (or when one of the 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.

Consumer vs. Producer

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:

Performance Events and Media Time

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.

Timing and SoundConsumer

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.

Timing and SoundProducer

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.

Parting Thoughts

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!


Organic Pace of Change

By Jean-Louis Gassée

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?

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