Be Newsletter
Issue 104, December 17, 1997
Table of Contents
BeOS Demo: MacWorld Expo'98, SD'98 in San Francisco, CA;
IT Forum/Comdex in Paris, France
- Be at MacWorld Expo/San Francisco
Tuesday, January 6 to Friday, January 9, 1998
Where:
Moscone Convention Center
South Hall
Booth #1847
For more information, see: http://www.macworldexpo.com/
- Be at IT Forum / Comdex
Monday, February 2 to Friday, February 6, 1998
Where:
Porte de Versailles
Paris, France
For more information, see: http://www.beeurope.com/
- Be at SD'98 (Software Development Conference '98)
Monday, February 9 to Friday, February 13, 1998
Where:
Moscone Convention Center
North Hall
Booth #454
For more information, see: http://www.sd98.com/
BE ENGINEERING INSIGHTS: Three Unrelated Tips
By Hiroshi Lockheimer
"The number of the counting shall be three, and three shall
be the number of the counting..."
1 -- DON'T FORGET THE "A" IN "RGB"
In the BeOS Preview Release 2, the default alpha arguments
to SetHighColor() , SetLowColor() , and SetViewColor() in
<View.h> were changed from 0 to 255. We also changed the
value of the constant B_TRANSPARENT_32_BIT . These changes
were made in long-range preparation (i.e., not for Release 3, and
not yet included in plans for any subsequent release) for
alpha channel support in the App Server and the Kits. This
means a number of things for you:
- You don't have to worry about
B_TRANSPARENT_32_BIT . It's
a symbol, so the change in value should be transparent
(pun intended) from a source compatibility standpoint.
We've added some filters in the aforementioned calls that
look for the old value, just to be extra safe.
- You should worry, however, if you have data (or, less
likely, code) that hard-codes the old
B_TRANSPARENT_32_BIT
value and you stuff that data directly into a BBitmap . The
solution here is...use the new value! (Duh.)
- You should inspect your code for uninitialized alpha
values. You don't have to do this immediately since we
currently ignore the value of the alpha component, but
it's a good habit to get into.
Here's some suspicious code:
// alpha component omitted!
rgb_color foo = {255, 255, 255};
If you don't want foo to be transparent (an alpha of 0), you
should do the following:
// explicitly set it to 255
rgb_color foo = {255, 255, 255, 255};
Here are some other common cases that I found while
rummaging through the Interface Kit:
{
rgb_color foo;
if (blah)
// this is good
foo = ViewColor();
else
// bad, alpha isn't being set...
foo.red = foo.green = foo.blue = 123;
}
{
// explicitly setting alpha to 0!
SetHighColor(80, 80, 80, 0);
...
}
2 -- BEWARE OF B_INTERRUPTED.
If you're writing an app that uses signals, or a library
that uses signals, or a library that may be used by an app
that uses signals, watch out for B_INTERRUPTED . Functions
such as acquire_sem() will return an error code of
B_INTERRUPTED when they are "unblocked" by a signal.
The following code is unsafe:
// theSem is a valid sem_id
acquire_sem(theSem);
Do this (or something like it) instead:
// loop until we acquire the semaphore
while (acquire_sem(theSem) == B_INTERRUPTED);
Here's a list of functions that you want to protect from
B_INTERRUPTED :
acquire_sem()
acquire_sem_etc()
port_buffer_size()
read()
write()
read_port()
read_port_etc()
write_port()
write_port_etc()
wait_for_thread()
3 -- ARE YOUR MESSAGES GETTING DELIVERED?
Speaking of error codes, did you know that
BLooper::PostMessage() returns an error? Or, to put it
another way, did you know that PostMessage() can fail, even
if all BLooper s and BHandler s involved are valid?
A BLooper 's port capacity is set to 100
(B_LOOPER_PORT_DEFAULT_CAPACITY ) by default. This is also
true of BWindow (BWindow inherits from BLooper ). If a
Looper's message queue is inundated with BMessage s, its port
fills up. Since a Looper with a full port can no longer
accept messages, that's one way a call to PostMessage() can
fail.
There are a number of ways to deal with this situation:
- Look at
PostMessage() 's return value. Handle errors
gracefully.
- Don't do any super time-consuming things in
MessageReceived() . Spawn a thread if you have to. This
reduces the likelihood of a BLooper 's port filling up.
- Use a
BMessenger .
BMessenger s are cool. The following code snippets are
functionally equivalent:
status_t err1 = myLooper->PostMessage(kMyCommandConstant);
BMessenger messenger(myLooper);
status_t err2 = messenger.SendMessage(kMyCommandConstant);
The difference is that the BMessenger version guarantees
that your message will be delivered, regardless of the state
of the recipient's port. It does this by blocking on the
full port until there is room. Keep in mind, this blocking
can lead to other issues such as deadlock, but that's been
covered in other articles, for example:
<http://www.be.com/aboutbe/benewsletter/Issue25.html>.
BE ENGINEERING INSIGHTS: MIDI Synthesis From Scratch
By Marc Ferguson
Past articles in the Be Newsletter have given example uses
of the MIDI and audio stream classes. This time we'll give
you an example for connecting a BMidiPort to a BDACStream to
build a real-time MIDI synthesizer that generates sounds
from scratch, instead of using the BSynth class as a sample
player. To try this example you'll need a MIDI keyboard and
a MIDI interface connected to your computer's printer port
(if you have a BeBox, use the midi2 input).
There are three parts to this example:
- a Voice class to generate the tones
- an Instrument class to receive the MIDI input
- a
dac_writer() "stream function" that writes to the DAC
stream buffers
The Voice class implements the Karplus-Strong synthesis
algorithm. It consists of a delay line with 100% feedback
through a low-pass filter, which averages adjacent samples.
Output samples look like this:
X[i] = (X[i-N] + X[i-N-1]) / 2
where N is the length of the delay line.
You can use any percussive sound to excite the delay line --
the Voice class just initializes the delay line to random
noise.
If you listen carefully you may notice that each note sounds
subtlely different. The wavelength of the tone produced is
about (N+0.5) and the frequency is sampling rate/(N+0.5).
Because N must be an integer, there is some rounding error
in the pitch of the output. The result is that lower notes
are in tune, while the higher notes are increasingly out of
tune. This is the best we can do without a fancier
algorithm.
In this example, the Voice::NoteOn() method initializes the
delay line and the Voice::Mix() method runs the delay line
and mixes the output into the destination buffer:
struct Voice {
Voice(int32 i) : number(i), velocity(0) {
delayLength = 44100 / 442.0 / pow(2.0, (i-9)/12.0 - 5);
delayLine = new float[delayLength];
};
~Voice() {
delete [] delayLine;
};
void NoteOn(uchar vel);
void Mix(int32 n_samples, float* dst);
int32 number;
uchar velocity;
int32 delayLength;
int32 delayPointer;
float* delayLine;
float previous;
};
void Voice::NoteOn(uchar vel)
{
if (vel > 0) {
/* Initialize delay line with noise */
delayPointer = 0;
for (int i = 0; i < delayLength; i++)
delayLine[i] = 160 * vel * (2.0*rand()/RAND_MAX - 1);
previous = 0;
}
velocity = vel;
}
void Voice::Mix(int32 n_samples, float* dst)
{
if (velocity > 0)
/* Mix samples from delay line into dst */
for (int i = 0; i < n_samples; i++) {
float current = delayLine[delayPointer];
dst[i] += current;
delayLine[delayPointer] = (current + previous) / 2;
if (++delayPointer >= delayLength)
delayPointer = 0;
previous = current;
}
}
The next component of the example, the Instrument class,
holds an array of Voices. It connects itself to a BMidiPort
and passes any NoteOn calls it receives to the appropriate
Voice. It also enters the DAC stream using dac_writer() as a
stream function:
#define LOWEST_NOTE 28
struct Instrument : BMidi {
Instrument();
~Instrument();
void NoteOn(uchar, uchar note, uchar velocity, uint32) {
acquire_sem(lock);
if (note >= LOWEST_NOTE)
voices[note]->NoteOn(velocity);
release_sem(lock);
};
void NoteOff(uchar channel, uchar note, uchar, uint32) {
NoteOn(channel, note, 0, 0);
};
Voice* voices[128];
sem_id lock;
BMidiPort port;
BDACStream dacStream;
BSubscriber subscriber;
};
Instrument::Instrument() : subscriber("Instrument")
{
lock = create_sem (1, "Instrument locker");
for (int i = LOWEST_NOTE; i < 128; i++)
voices[i] = new Voice(i);
dacStream.SetStreamBuffers(1024, 2);
dacStream.SetSamplingRate(44100);
subscriber.Subscribe(&dacStream);
bool dac_writer(void*, char*, size_t, void*);
subscriber.EnterStream(0, true, this, dac_writer, 0, true);
if (port.Open("midi2") != B_NO_ERROR) {
printf("Failed to open midi port.\n");
return;
}
port.Connect(this);
if (port.Start() != B_NO_ERROR) {
printf("BMidiPort::Start() failed.\n");
return;
}
}
Instrument::~Instrument()
{
subscriber.ExitStream(true);
for (int i = LOWEST_NOTE; i < 128; i++)
delete voices[i];
delete_sem(lock);
}
Finally, the dac_writer() stream function allocates a mixing
buffer of floats, calls the Mix() method for each voice, and
mixes the result into the DAC stream:
bool dac_writer(void* arg, char* buf, size_t count, void*)
{
short* dst = (short*) buf;
int n_samples = count/4;
Instrument* instrument = (Instrument*) arg;
static int32 mix_samples = 0;
static float* mix = 0;
if (mix_samples < n_samples) {
if (mix)
delete [] mix;
mix = new float[n_samples];
mix_samples = n_samples;
}
for (int i = 0; i < n_samples; i++)
mix[i] = 0;
acquire_sem(instrument->lock);
for (int i = LOWEST_NOTE; i < 128; i++)
instrument->voices[i]->Mix(n_samples, mix);
release_sem(instrument->lock);
for (int i = 0; i < n_samples; i++) {
int32 sample = mix[i];
int32 left = dst[2*i] + sample;
int32 right = dst[2*i+1] + sample;
if (left > 32767) left = 32767;
if (right > 32767) right = 32767;
if (left < -32768) left = -32768;
if (right < -32768) right = -32768;
dst[2*i] = left;
dst[2*i+1] = right;
}
return true;
}
Now, if you create an Instrument object, you should be able
to play a MIDI keyboard connected through your computer's
printer port.
One other thing you need to be concerned about if you're
writing a real-time synthesizer is the latency between the
time a key is struck on the keyboard and when you hear the
note. This latency includes the time it takes the keyboard
to notice that a key has been pressed, the time to transmit
three bytes via MIDI, the time to calculate the waveform,
and the amount of output buffering for the DAC. The amount
of DAC buffering is set by the call to SetStreamBuffers() in
the Instrument() constructor. The smaller the DAC buffers,
the more CPU time is spent handling DAC interrupts.
In the example, two buffers of 1K bytes (or 256 samples) are
used for a total of about 12 milliseconds of buffering.
These 1K byte buffers are large enough to run well on a
Dual-66 BeBox and small enough that the latency is not
unreasonable. If you have a faster machine, you can
experiment with smaller buffer sizes and shorter latencies.
DEVELOPERS' WORKSHOP: The Postman Always Beeps Twice
(At Least When You Have Mail)
By Eric Shepherd
"Developers' Workshop" is a new weekly feature that
provides answers to our developers' questions. Each week, a
Be technical support or documentation professional will
choose a question (or two) sent in by an actual developer
and provide an answer.
We've created a new section on our website. Please send us
your Newsletter topic suggestions by visiting the website
at: http://www.be.com/developers/suggestion_box.html.
Handling e-mail in BeOS applications is extremely easy. The
BMailMessage class provides a quick and painless way to send
mail messages. Reading messages is a little harder, but not
by a lot. This week we'll look at how you can take charge of
the Mail Kit so your applications can send messages and
query the system about received messages.
The BMailMessage class represents an outgoing e-mail. You
can add a message body, enclosures, and header fields to the
message using the functions in the BMailMessage class, which
is described in lovely C++ header file form in <E-mail.h> .
REACH OUT
Let's write a simple C function to create and send an
e-mail, which we'll cleverly call "sendmail ".
status_t sendmail(char *to,
char *subject,
char *from,
char *replyto,
char *content,
char *enclosure_path) {
BMailMessage *message;
status_t err;
Our sendmail() function lets us specify the To, Subject,
From, and Reply-to header fields, as well as the message
content and one enclosure, specified by pathname.
message = new BMailMessage();
if (!message) {
// Couldn't allocate memory for the message
return B_ERROR;
}
First, we create a new BMailMessage object. If we can't
create it, we just return B_ERROR immediately, since there's
really no point in going on.
The To and Subject header fields are mandatory for any
e-mail message, and the Mail Kit doesn't know how to create
them for you automatically, so you need to set them up:
err = message->AddHeaderField(B_MAIL_TO, to);
err |= message->AddHeaderField(B_MAIL_SUBJECT, subject);
Notice that we're ORing the result codes of the
AddHeaderField() calls together. In this example program, we
don't care *what* error occurred, but only whether or not an
error occurred at all. By OR ing all the results together, we
obtain a mega-result that's B_OK (which happens to be zero)
if and only if no errors occurred at all.
Of course, you could be more programmatically correct and
handle each error individually with alerts, but this saves
some space for the purposes of this article without
disposing of error-handling entirely.
The From and Reply-to fields can be automatically filled in
by the Mail Kit if you don't specify them. So if the caller
specifies NULL for these two fields, we don't bother to set
them up.
if (from) {
err |= message->AddHeaderField(B_MAIL_FROM, from);
}
if (replyto) {
err |= message->AddHeaderField(B_MAIL_REPLY, replyto);
}
Then we add the message body by calling AddContent() :
err |= message->AddContent(content, strlen(content));
And, if the enclosure_path parameter is non-NULL, we add the
enclosure to the message:
if (enclosure_path) {
err |= message->AddEnclosure(enclosure_path);
}
Finally, if no errors have occurred, we send the message by
calling Send() . The first parameter to Send() is a boolean
that specifies whether the message should be sent
immediately (true) or queued to be sent the next time the
mail daemon is scheduled to run (false). In this case, we
want the message to be sent immediately, so we specify true.
if (err == B_OK) {
err = message->Send(true); // Send now
}
Now it's time to clean up. We delete the message object and
return B_ERROR if an error occurred or B_OK if everything
went well:
delete message;
if (err) {
return B_ERROR;
}
return B_OK;
}
Now you can send a message by calling sendmail() :
status_t error = sendmail("foo@bar.com",
"Subject",
"sheppy@be.com",
"sheppy@bar.com",
"This is the message body.",
"/boot/home/file.zip");
This will send, to foo@bar.com, the message "This is the
message body." with the file "/boot/home/file.zip " attached.
The From will be listed as sheppy@be.com, and the Reply-to
will be sheppy@bar.com.
You could also do:
status_t error = sendmail("foo@bar.com",
"Subject",
NULL,
NULL,
"Body",
NULL);
This sends a message to foo@bar.com with a subject "Subject"
and the body "Body." There's no enclosure, and the Mail Kit
automatically sets From and Reply-to to the addresses
configured in the E-mail preferences application.
Read the <E-mail.h> header file to see what other header
fields you can add to your messages.
SPECIAL DELIVERY
When e-mail arrives, the mail daemon creates a new file to
contain the message and adds the appropriate attributes to
the file. These attributes contain things like the subject,
from, to, and other fields. With these attributes firmly in
place, you can query the file system to look for e-mail
files based on the values of these fields.
Let's create a sample program that lists the subjects and
senders of all unread messages. We'll call it
listmessages() . You should already be familiar with queries
and attributes. If you're not, have a look at the BQuery and
BNode sections in the Storage Kit chapter of the "Be
Developer's Guide."
void listmessages(void) {
BQuery query;
BNode node;
BVolume vol;
BVolumeRoster vroster;
entry_ref ref;
char buf[256];
int32 message_count = 0;
New messages are stored by the mail daemon on the user's
boot disk, so we begin by using the volume roster to
identify the boot disk and set our query to search on that
disk:
vroster.GetBootVolume(&vol);
query.SetVolume(&vol);
The next thing we do is set up our query. We want to search
for new mail messages, so our search predicate is
"MAIL:status = New". See the <E- mail.h> header file for a
list of the mail message attributes.
If an error occurs while trying to establish the predicate
for the query, we bail out:
if (query.SetPredicate("MAIL:status = New") != B_OK) {
printf("Error: can't set query predicate.\n");
return;
}
Then we run the query. If an error occurs, we print an
appropriate message and give up:
if (query.Fetch() != B_OK) {
printf("Error: new mail query failed.\n");
return;
}
Now it's time to loop through the files in the query's
result set. We'll use BQuery::GetNextRef() to iterate
through them. For each entry, we set up a BNode so we can
use BNode 's attribute functions to read the file's
attributes. Again, as good programmers, we gracefully cope
with errors:
while (query.GetNextRef(&ref) == B_OK) {
message_count++; // Increment message counter
if (node.SetTo(&ref) != B_OK) {
printf("Error: error scanning new messages.\n");
return;
}
Next, we read the From attribute. This attribute's name is
given by the constant B_MAIL_ATTR_FROM . We use a 256-byte
buffer, preinitialized to an empty string, and call
BNode::ReadAttr() to read the attribute into the buffer.
To facilitate formatting our display, we chop off the From
string at 20 characters, then print the message number and
the From attribute to the screen:
buf[0] = '\0'; // If error, use empty string
node.ReadAttr(B_MAIL_ATTR_FROM, B_STRING_TYPE, 0, buf, 255);
buf[20] = '\0'; // Truncate to 20 characters
printf("%3d From: %-20s", message_count, buf);
Our loop ends by reading the Subject attribute
(B_MAIL_ATTR_SUBJECT), truncating it to 40 characters, and
printing it as well:
buf[0] = '\0'; // If error, use empty string
node.ReadAttr(B_MAIL_ATTR_SUBJECT, B_STRING_TYPE, 0, buf, 255);
buf[40] = '\0'; // Truncate to 40 characters
printf(" Sub: %s\n", buf);
}
To add a little panache, we round things out by displaying
the total number of new messages. If we didn't find any new
messages, we say so:
if (message_count) {
beep(); // The postman beeps once
printf("%d new messages.\n", message_count);
beep(); // The postman beeps twice
}
else {
printf("No new messages.\n");
}
The output from this function looks something like this. The
"<she" is the beginning of my e-mail address, chopped off by
our 20-character truncation of the From attribute:
1 From: "Eric Shepherd" <she Sub: Newsletter test
2 From: "Eric Shepherd" <she Sub: re: BeOS programming
2 new messages.
Consider this: In the while loop, you obtain an entry_ref
for every new e-mail on the user's system. Instead of
listing out the subject and sender's names, you could just
as easily open up the file and show the contents in a
window. You're well on your way to having an e-mail reader
without even breaking a sweat (and we all know how expensive
it is to replace broken sweat).
Some Thoughts for the New Year
By Dave Johnson
WE HAVE APPS
I'm sure you've all heard the "chicken-and-egg" paradox,
which has it that developers won't do applications for the
BeOS before there is an installed customer base, and
customers won't install the BeOS until there are
applications.
Well, I'm pleased to say that our "egg" days are over and
we're hatching. At our Macworld Expo booth in January,
several software companies will demonstrate compelling
applications that let you do things you cannot do on Mac or
Windows, that out-perform Mac and Windows apps, and that
generally prove the BeOS is a superior platform for media
applications.
MORE APPS ARE IN THE PIPELINE
There are more applications coming. I can't give you
specifics, except to say that it's time to change the way
you think about the BeOS. As we expand into the Intel world
there will no longer be any basis for the criticism "but
they have no apps." (I don't yet know what criticism will
take its place, but it won't be that.) The Intel port
represents a big change in how we see the BeOS, and we need
to adjust our brains around it.
But please don't replace "I don't think I'll develop an app
for the BeOS because there aren't any apps" with "I don't
want to develop for the BeOS because someone is probably
already doing the app I want to do." The BeOS running on
Intel offers a huge opportunity -- and you have a head
start. The field is wide open for you to write your dream
application with minimal competition.
THE BEOS IS NOT A MAC UTILITY
The BeOS has always been a separate operating system, even
if it happened to run on the Macintosh. More and more,
you'll see us listed as an independent platform alongside
Mac, Windows, Unix, Linux, DOS, etc...
THE BEOS IS FIBER AND CD
Some of us have been working with the BeOS for so long that
we don't remember the experience of discovering it for the
first time. Seeing and using the BeOS for the first time is
one of those defining moments, paradigm shifts, flash
realizations, spiritual transformations...
I saw this again as I watched someone here give his first
demo last Friday. He was a little nervous and awkward,
looking at his script, hesititant, but his confidence was
sustained by a guy in the audience who kept shouting, "MY
GOD!" and "OH MY GOD!!!" every time he showed another BeOS
feature.
The BeOS is the equivalent of a fiber-optic infrastructure
upon which your media applications are built, bringing a
quantum increase in performance. Mac and Windows are old
copper wire systems. Mac and Windows are vinyl and the BeOS
is CD.
Sure, there's a lot of support out there for copper wire
systems, just like there was a huge inventory of vinyl
recordings. Hundreds of manufacturers build things like
insulators and transformers for telephone poles, wire
cutters, connector boards...plus lots of electricians,
etc...
Because fiber is new technology there is not that kind of
support structure in place yet. (The infrastructure is
coming for the BeOS because YOU are going to develop
products and drivers and make a lot of money -- right?)
But this isn't like Beta vs. VHS, where both are acceptable
so either one can win. This is fiber vs. copper and it is a
matter of spreading the word, getting the awareness out
there. It is a question of "when," not "if." From now on
media apps must be on the BeOS to be competitive. The BeOS
*is* the Media OS.
Tectonic Plates
By Jean-Louis Gassée
I was wrong when I assumed Judge Jackson would take his time
ruling on the Department of Justice complaint. Rule he did,
but the exact meaning of the ruling evades yours truly; the
writ appears to state that either party could ultimately win
the case. Judge Jackson grants a temporary injunction but
not the fine requested by the DOJ, and appoints a Special
Master, Lawrence Lessig, a noted cyberspace jurist, to
render a more definite ruling by May 1998. Microsoft
appeals, criticizes the ruling, and proposes to comply in
such a manner as to raise eyebrows. Strange.
Two less immediately controversial items recently piqued my
amateur kremlinologist interest. One is the spin TCI puts on
its next generation set-top box efforts, the other is the
Sun-Intel pact around Merced, the next generation high-end
microprocessor. Do these shifts of the industry tectonic
plates signal a distancing from Redmond?
Let's start with TCI. The company is in the final phase of
defining its new set-top box platform. Until recently,
Windows CE was assumed to be the obvious choice for the OS.
What we now have from TCI's PR department is a mille-feuille
-- a sandwich of hardware, operating system, add-ons and
applications, with diagrams naming several candidates for
each layer. The processor could come from Intel, Motorola,
SGS, LSI Logic and so on; the OS from Microsoft or Oracle's
NCI; systems would be made by NextLevel (nee General
Instruments), Scientific Atlanta, Sony; program guides from
Microsoft, StarSight, Thomson for starters; and the service
providers would range from @Home to AOL, TCI itself, NBC and
AT&T among a plethora of contenders. The spin is that TCI
wants this to be a very open platform (with a capital "Oh").
Open? Or simply closed to (the perception of) Microsoft
domination? After all, this isn't the best time to get
caught in a loving embrace with a company whose investment
in the cable business has already caused concern. If Windows
CE (Consumer Electronics) becomes the Windows of set-top
boxen, another game is over, critics say. The anti-trust
division of the DOJ, already in a bellicose mood, would not
be charmed.
So the claim of "openess" could be mere posturing. We'll
see. In the meantime, let's crack open the history books...
You could say TCI is trying to reproduce in vitro what
happened in the personal computer business: In the
beginning, there were all sorts of processors and operating
systems. Over the years, an elimination process took place
and got us where we are, with Wintel running on most
desktops. Now, TCI wants to play a disinterested Mother
Nature as the set-top biosphere settles on its fittest
survivors. Of course, the unabetted process of elimination
isn't a spectator sport. (Geeks might enjoy the show, but
consumers won't.) If TCI wants success with couch potatoes
as well as Internet users, they'll have to limit the number
of permutations. My guess is that they know this, and they
will try to nudge the dice. As long as they have the
perception of competition without actually losing control of
the strings -- which implies something other than Windows
CE.
The other shift happening this week is the Sun-Intel pact,
or the Solaris-Merced agreement. Merced is the grand
unifier, developed as the successor to the Pentium as well
as HP's RISC Precision Architecture. It will run both legacy
instruction sets, plus a new, more advanced one. Today's
announcement means, among other things, Sun's system
software will run on Merced ("optimized" the release says,
as if it would tell you if they had done a quick, dirty, and
dispirited port).
Big announcements of big alliances sometimes produce just
that, the announcement. Even if the alliance holds, this is
something of a non-event -- who's going to not support
Merced? Or it may be a defensive move on Sun's part: After
failing to achieve anything of market consequence on Intel
processors, an Intel/HP duopoly in the juicy server business
doesn't feel good.
Another interpretation, not necessarily exclusive, is that
Intel likes the idea of using Merced as an opportunity to
change the power structure they've profited from, but not
always unequivocally enjoyed, in the PC business. After all,
Windows CE and NT work on other processors -- why shouldn't
Intel work with more OS partners?
For Intel to approach the server business with Merced, HP's
Unix, Sun's Solaris, and Windows NT must feel good. And we
might remember that all new Intel chips are initially
positioned for server and other high-end applications, the
current ones being ideal for mainstream desktop
applications. That was the story for the 386, the 486, the
Pentium and the Pentium II. Soon enough, with process
prowess, they become affordable for the mainstream. So will
Merced.
And Microsoft, seeing all this, will keep claiming they have
much competition to worry about.
|