Be Newsletter
Issue 73, May 14, 1997
Table of Contents
BE ENGINEERING INSIGHTS: Nino Redux: Porting Audio to the Preview Release
By Marc Ferguson
An article in a previous newsletter (Issue 36) contained
sample code for the Nino class, a simple audio test bed
which copies audio from the ADC input stream to the DAC
output stream.
The audio API has changed slightly in the Preview Release.
This article will illustrate the more important API changes
by showing how the Nino class should be rewritten for the
Preview Release.
The old Nino class definition contained a buffer, two
semaphores for controlling the stream functions, and two
BAudioSubscriber objects, one for the ADC stream and one for
the DAC stream. In the Preview Release, each
BAudioSubscriber object has been replaced by two new
objects, a generic BSubscriber object and either a
BADCStream or BDACStream object. Thus the new Nino class
contains one BADCStream , one BDACStream , and two
BSubscribers :
struct Nino
{
Nino ();
~Nino ();
short* adc_buffer;
sem_id bufferReady;
sem_id bufferUsed;
BADCStream* adcStream;
BDACStream* dacStream;
BSubscriber* adcSubscriber;
BSubscriber* dacSubscriber;
};
There are two "stream functions," one for each stream,
called adc_reader() and dac_writer() . The stream functions
have a new "header" argument which provides access to a
timing estimate for each buffer, but otherwise they work as
before. adc_reader() uses the bufferReady semaphore to
indicate that an input buffer has been received, and
dac_writer() uses the bufferUsed semaphore to signal that it
has finished merging the input buffer into the output
buffer.
static bool
adc_reader (void* arg, char* buf, size_t count, void* header)
{
Nino* my_nino = (Nino*) arg;
my_nino->adc_buffer = (short*) buf;
release_sem (my_nino->bufferReady);
acquire_sem (my_nino->bufferUsed);
return TRUE;
}
static bool
dac_writer (void* arg, char* buf, size_t count, void* header)
{
Nino* my_nino = (Nino*) arg;
short* dac_buffer = (short*) buf;
acquire_sem (my_nino->bufferReady);
for (int i = 0; i * sizeof(short) < count; i++) {
int32 sample = (int32) dac_buffer[i]
+ my_nino->adc_buffer[i];
if (sample > 0x7FFF)
sample = 0x7FFF;
if (sample < -0x7FFF)
sample = -0x7FFF;
dac_buffer[i] = (short) sample;
}
release_sem (my_nino->bufferUsed);
return TRUE;
}
To simplify the example, these functions assume that both
streams have the same buffer size. Be careful not to erase
whatever may already be in the output stream. dac_writer()
is being a good citizen and mixing the ADC stream into the
output rather than overwriting it.
In the Preview Release, the ADC stream and DAC stream always
contain 16-bit stereo samples. The functions
SetADCSampleInfo() and SetDACSampleInfo() no longer exist.
The sampling rate can still be changed. The default sample
rate is 44.1kHz and this rate should be used when practical.
The Nino constructor initializes the semaphores, streams,
and subscribers and enters the subscribers into their
respective streams.
Nino::Nino()
{
bufferReady = create_sem (0, "buffer ready");
bufferUsed = create_sem (0, "buffer used");
adcStream = new BADCStream();
dacStream = new BDACStream();
adcSubscriber = new BAudioSubscriber ("Nino ADC reader");
dacSubscriber = new BAudioSubscriber ("Nino DAC writer");
adcSubscriber->Subscribe (adcStream);
dacSubscriber->Subscribe (dacStream);
adcSubscriber->EnterStream (NULL, FALSE,
this, adc_reader,
NULL, TRUE);
dacSubscriber->EnterStream (NULL, TRUE,
this, dac_writer,
NULL, TRUE);
}
Note that the Subscribe() function has been simplified in
that there is no longer a "clique" argument. EnterStream()
works as before.
The Nino destructor carefully waits for dacSubscriber to
exit before deleting the semaphores:
Nino::~Nino()
{
dacSubscriber->ExitStream (TRUE);
delete_sem (bufferReady);
delete_sem (bufferUsed);
adcSubscriber->ExitStream (TRUE);
delete adcSubscriber;
delete dacSubscriber;
delete adcStream;
delete dacStream;
}
To test a Nino using an audio CD as input, open the sound
preference panel and select "CD" as the input source, turn
down the input gain, and turn off the loopback. Put an
audio CD in your CD drive and use the CDPlayer application
to play it. You can now experiment with digital audio
processing by modifying the code in the dac_writer()
function.
To summarize, the most important changes which have to be
made when porting audio to the Preview Release are replacing
the BAudioStream object with a pair of objects (BSubscriber
and BADCStream or BDACStream ), adding a "header" parameter
to the stream functions, and always using the 16-bit stereo
sample format.
BE ENGINEERING INSIGHTS: Attributes, File And Human
By Geoff Woodcock
I had a conversation with a friend of mine who works for a
well-known international high-tech company. He told me a
story about the way this company works. "There isn't a
single guy in my group who knows what the ---- is going on.
Management wants me to save them, but I know I don't know
anything either." But, apparently he knows more than most of
them. "I wrote an application note, I didn't think anything
about it,' he said. "Two weeks later, my boss tells me that
<name deleted> turned it in as his own work." He continued
for several minutes, ending with, "What you know doesn't
mean ----, it's all politics."
Maybe I'm just naive for being shocked at this. But it's a
useful reminder of why I like working at Be so much.
Knowledge is definitely more important than rank. Nobody
could get away with that stuff here. I'm feeling insecure
because I'm pretty sure the VP of sales is a better
programmer than I am. I get PC configuration tips from the
CEO. These people do not get their credibility from their
titles. This atmosphere is what gives all of us the courage
to test conventional wisdom by gambling on something that
appeals to our common sense. Let's step over the gangrenous
bodies of these other companies. Like they say, those who
die in train wrecks are the ones who were afraid to get on a
jet.
The DR9 file system has changed a lot. Besides everything
else, the database-like structure you knew has been
completely replaced. So, what to do if you were using the
database?
The new, attributed file system still has features that can
be used like a database. You can do queries. A query is just
a set of Boolean operators connecting "<name> = <value>"
strings, where <name> is an attribute name. A query can only
be executed on a single volume at a time. Queries return
lists of entry_ref s, although this may change. Paths may
become the standard currency for referencing things in the
file system.
Here's an example, from BTSQueryWindow.cpp in the sample app:
// Create a query object.
BQuery* query = new BQuery();
// Get the query string from the text box
const char* queryString = mQueryField->Text();
// Figure out which volume is selected.
BMenuItem* item = mVolumeMenu->FindMarked();
int32 index = mVolumeMenu->IndexOf(item);
const BVolume volume((dev_t)mDeviceList.ItemAt(index));
// Define the query.
query->SetPredicate(queryString);
query->SetVolume(&volume);
// Execute the query.
query->Fetch();
The query is just a list of entry_ref s. Here's how to show a
list of paths in a listbox, from BTSResultWindow.cpp:
BEntry entry;
BPath path;
// Start at the beginning.
mQuery->Rewind();
// Step through the entries
while (mQuery->GetNextEntry(&entry) == B_NO_ERROR)
{
// Get the path, put it in the listbox.
entry.GetPath(&path);
const char* pathName = path.Path();
BStringItem* item = new BStringItem(pathName);
mFileListView->AddItem(item);
}
Once you figure out which file you want, you've got to scan
for the attributes you're interested in. Here's some code
that puts all the attributes for an entry_ref into a
listbox, from BTSAttrListWindow.cpp:
// Make node from BEntry object.
BNode node(mEntry);
while(node.GetNextAttrName(attrName) == B_NO_ERROR)
{
char* name = strdup(attrName);
// Get the attribute info (type, size)
node.GetAttrInfo(attrName, &info);
// Create a string to put in the listbox
strncpy(type, (char*)&(info.type), 4);
sprintf(listLine, "\"%-32.32s\" \'%-4.4s\' %-9.9Ld",
attrName, type, info.size);
BStringItem* item = new BStringItem(listLine);
// Add the item to the listbox
mAttrListView->AddItem(item);
mAttrNameList.AddItem(name);
}
Adding and removing attributes is easy. Just figure out the
name, type, and data, and create a BNode that references the
file to which you want to assign the attribute. A BNode can
be created from a path, a BEntry , an entry_ref , a BNode , or
a BDirectory . Here's writing and reading attributes, from
BTSAttrView.cpp:
// The window keeps track of the selected BEntry in the
// mEntry member.
BNode node(mEntry);
void* data;
size_t size;
uint32 type;
// Get the data, size, and type from the currently
// displayed view.
mCurView->GetData(&data, &size, &type);
// If it's not a new attribute, and the new attribute is
// smaller in size, you have to remove the attribute to
// resize it.
if (!mNewAttr) node.RemoveAttr(mAttrName);
// just write the data.
node.WriteAttr(mAttrName, type, 0, data, size);
So, if you want a database table, an easy approach is to
make a directory, and create one file per record. (Of
course, there's no reason they have to be in the same
directory. If your app needs to spread things around, go
ahead!) The attributes on the files represent the columns of
the record. Records created in this way don't have to all
have the same columns. Also, remember when you overwrite an
existing attribute that you may need to remove it first, if
you want to reduce the size of the attribute.
Attributes can be indexed for fast searching. Only int32,
int64, float, double, and string types can be indexed, and
indices are placed on the volume. Also, when you create a
new index, existing attributes don't get added to it; you
have to rewrite the attributes to add them to the index. You
should see a sample in
ftp://ftp.be.com/pub/samples/storage_kit/obsolete/IndexUpdater.tar.gz that
will update indices for you.
A few of the current concerns with attributes. If you have a
bunch of small attributes, they may be space inefficient.
There's a ~780 byte reserved area for initial attributes
(for a volume with 1024 byte blocks), but additional attributes
will require an extra file block. Also, you can't have two
attributes with the same name, which could compound the size
problem. If you need to do this, you'll probably want to
pack multiple attributes in a single buffer and store them
under one name.
One thing that's still a little up in the air is, what is
the standard way to store complex attributes? Anybody can
pull up a string attribute, but what about a bitmap? Is the
color space the first thing in the data buffer? What about a
BRect ? This becomes important if different apps want to look
at and edit attributes of unrelated files. There are also
as yet no standards for attribute names; how do you find an
email address, is it "email" or "e-mail"? If it becomes
necessary, Be will publish guidelines.
One simple way to put just about anything in an attribute is
to make it archivable. Something that's archivable has a
constructor that takes a BMessage and implements the
Archive(BMessage* message ) method. Since messages can still
be flattened to a buffer, you can store just about anything
as an attribute and have an easy way to recreate it, getting
the other benefits of archiving to boot!
What else can be done with attributes? You've got to think
differently, you've probably never before had a feature like
this, built right into the OS to exploit. What about an
OpenDoc-like document draft system, where old revisions of
the document are stored as diffs in attributes? What about a
compiler that puts function/method meta-data in attributes
so you can search it easily? How can your apps share
information to make them better? Any time your document/app
has meta-data, consider putting it in an attribute instead
of in the file.
Lastly, when should you use attributes, when should you use
resources? Resources are not searchable. Attributes are less
portable, as other filesystems may not support attributes.
This problem with attributes will be mitigated by tools like
zip that know how to include attribute information. When in
doubt, use an attribute.
If you want to see a working demo using attributes, download
ftp://ftp.be.com/pub/samples/AttrBrowser.tar.gz, a
simple, database-browser type app that supports the editing
of a few attribute types. To put a bitmap in an attribute,
try dragging and dropping one from Rraster.
News from the Front
By William Adams
We've done the impossible, or at least something that is of
historical significance to the company. We have completed
our first 2 day World Wide Developer's conference. For
those of you who attended, I hope you all had a good time
and learned a lot. For those of you who couldn't make it, I
hope you can make it to MacHack or Boston MacWorld.
We had all the drama that you would expect for a major
event. Many engineers staying up late nights, demos not
working until the last minute. Slides not coming up. But
in the end, I think we made a good accounting for ourselves
and disseminated a lot of very useful information. We had
our first Master's awards program, the results of which will
be posted on the web site shortly. We also had a chance to
form new alliances, and impress a flock of new developers.
If you were at the Conference, you received a Early Access
CD. Judging by our email, you've gotten it onto your
machine without too much difficulty, and are now looking
around for the documentation. You should also find on the
CD a number of sample applications. There are some old
familiar ones, as well as some DR9 specific new ones. You
should be able to mine these sources for useful tips on
DR9isms. In the coming weeks we will be working very hard
to complete the documentation and write more sample apps;
until then, send those pressing questions to
DevSupport, via the online Developer Support form in the Registered Developers Area and we'll try to help you out. In many
cases we might refer you to one sample app or another. If
that doesn't help, then tell us so and we'll go further.
During any new release, there are questions and concerns.
We'll do our best to keep you informed and programming
towards the Preview Release.
Coming up, we have three days of conferences in Europe, Mac
Hack in June, and MacWorld in August. This makes for a very
busy summer. We want you programmers to work real hard,
because the harder you work, the faster we can show your
apps, and the faster they get exposure, the better chance
they will have of making money for you so that you can
continue to program for the BeOS.
Our short term job is not over yet. It's only just begun.
This week we will be preaching to the Mac faithful at the
Apple World Wide Developer's Conference. I look forward to
what will hopefully be a favorable reception and a community
building experience.
Again, for those of you who couldn't make this conference,
keep up the good work, and we'll see you in Boston. For
those of you who did make it, you now know that Yams truly
does exist, and she is truly the littlest geek. She has the
tatoos to prove it.
And Now
By Jean-Louis Gassée
We are just emerging from last weekend's conference, a
successful one. On the opening day, Saturday May 11th, we
tallied over 650 developers coming from the US, mostly, but
also Europe and Japan. This is, as a ZDNet report noted,
twice as many as at our conference right after MacWorld, in
January of this year. And, Sunday night, there were more
than two thirds of the initial audience in the room for the
closing session. On Mother's day... What this means depends
upon your perspective. We have no life, no feelings of
guilt, or the conference had something going for it. It
wasn't the food, we made sure of that, nor the location, no
Palm Springs in the lowlands of Santa Clara.
Seriously, how not to be ecstatic, just for a moment, before
we return to our usual oscillations between excitement and
anxiety. Last week, I wrote we were going to appear before
a jury of our peers: skeptical, competent programmers. The
interest they demonstrated throughout the entire conference
was the best reward we could hope. It's one thing to
believe in our work -- we do, the belief has carried us
through "interesting" times. Feeling the enthusiasm of
people we respect takes our motivation to another level.
The feedback we received from the January conference has
been very useful. In particular, we heeded the advice to put
more technical content in our presentations and to create
parallel "tracks", simultaneous presentations broadening the
range of topics.
Early indications are that this was well received. We had
our share of glitches with hardware, software and sound
systems, probably less than we had a right to expect. No
hugely embarrassing crashes, this will be for an even bigger
occasion.
As we grow, I'd like us to continue with an effort to put on
a professional show for our audience, but, at the same time,
I wouldn't want to get too far on the side of gloss. It's a
bad sign when programming manuals start looking like
Cadillac brochures. The same goes for the venue. Our use
of a weekend creates a burden, but we got great rates on
everything, including airfare. I heard the hotel charged
less than one third the rate it did the following week for
the bigger company's event.
In many conferences, it is what takes place between
sessions, the hallway schmoozing, that makes the value of
the event. We haven't yet facilitated enough of this, it
seems. It usually requires a resort, an off-site location
where people stay for a couple of days. We would have to
find creative ways to make it affordable without ending in a
bad suburb of Paris.
In the same vein of encouraging two-way communication, the
"plenary" Q&A session at the end will demand rethinking. A
larger group will make it unwieldy and/or superficial.
Those are great problems. And we need your help in
addressing them in the right combination of professional and
unconventional fashions. Venues, content, calendar, style,
length, participants, guests, media, everything is open to
re-examination. But for the central goal, creating the
conditions for Be developers to succeed, and for us to rise
with the tide as a result.
I will not leave the topic without expressing again my
heartfelt thanks to Be developers who give meaning to our
lives, and to my colleagues at Be for the best work I have
ever been a part of.
Now, enough ecstasy, we have to deliver.
|