A number of people have expressed interest in writing sound card drivers for BeOS. This article describes the interface used by the current audio server to communicate with sound card drivers. Note that this interface is for the current audio server, and that the current audio server will be replaced with something better in a future release. But if you want to write a sound card driver and test it with the current audio server, this is what you will have to support.
These are the ioctl codes used by the audio server:
#include <Drivers.h> enum {SOUND_GET_PARAMS
=B_DEVICE_OP_CODES_END
,SOUND_SET_PARAMS
,SOUND_SET_PLAYBACK_COMPLETION_SEM
,SOUND_SET_CAPTURE_COMPLETION_SEM
,SOUND_RESERVED_1
, /* unused */SOUND_RESERVED_2
, /* unused */SOUND_DEBUG_ON
, /* unused */SOUND_DEBUG_OFF
, /* unused */SOUND_WRITE_BUFFER
,SOUND_READ_BUFFER
,SOUND_LOCK_FOR_DMA
};
The SOUND_SET_PLAYBACK_COMPLETION_SEM
ioctl
takes a (sem_id*
) argument
which points to a semaphore that must be released once for each buffer
written. The semaphore should be released when the data in the buffer is
no longer needed.
The SOUND_SET_CAPTURE_COMPLETION_SEM
ioctl
takes a (sem_id*
) argument
which points to a semaphore that must be released once for each buffer
read. The semaphore should be released when the data in the buffer is
valid.
The SOUND_WRITE_BUFFER
ioctl takes
an (audio_buffer_header*
) argument
which is defined in MediaDefs.h
:
typedef struct audio_buffer_header { int32buffer_number
; int32subscriber_count
; bigtime_ttime
; int32reserved_1
; int32reserved_2
; int32reserved_3
; int32reserved_4
; } audio_buffer_header;
The audio data immediately follows the audio_buffer_header in
memory and is in stereo signed 16-bit linear native-endian format. The size
in bytes of the audio data plus the audio_buffer_header is stored in the
reserved_1
slot of the audio_buffer_header
(the audio server was written before the size argument to the ioctl call
was implemented). The size and address of the audio data can be derived
this way:
audio_buffer_header*header
= (audio_buffer_header*)ioctl_arg
; int32bytes_of_data
=header
->reserved_1
- sizeof(*header
); int16*addr_of_data
= (int16*) (header
+ 1);
The driver can ignore the buffer_number
and subscriber_count
slots of
the buffer header and should store an estimate of the
system_time()
corresponding to the beginning of the buffer in the
time
slot of the
buffer header.
The SOUND_WRITE_BUFFER
call is allowed to return
before the data in the buffer has been consumed but the playback completion
semaphore must be released when the buffer can be recycled.
The SOUND_READ_BUFFER
ioctl takes an
(audio_buffer_header*) argument. The size and address of the
data can be computed as above. The time
slot should be
written with the estimate of the system_time()
corresponding to the beginning of the buffer. The call is allowed to return
before the buffer is full but the capture completion semaphore must be
released as soon as the buffer is full.
The SOUND_GET_PARAMS
and
SOUND_SET_PARAMS
ioctls read and write a set of
parameters which correspond to the settings in the sound preferences panel.
The ioctl argument is a (sound_setup*) in the following
format:
enum adc_source { line = 0, cd, mic, loopback }; enum sample_rate { kHz_8_0 = 0, kHz_5_51, kHz_16_0, kHz_11_025, kHz_27_42, kHz_18_9, kHz_32_0, kHz_22_05, kHz_37_8 = 9, kHz_44_1 = 11, kHz_48_0, kHz_33_075, kHz_9_6, kHz_6_62 }; enum sample_format {}; /* obsolete */ struct channel { enum adc_sourceadc_source
; /* adc input source */ charadc_gain
; /* 0..15 adc gain, in 1.5 dB steps */ charmic_gain_enable
; /* non-zero enables 20 dB MIC input gain */ charcd_mix_gain
; /* 0..31 cd mix to output gain in -1.5dB steps */ charcd_mix_mute
; /* non-zero mutes cd mix */ charaux2_mix_gain
; /* unused */ charaux2_mix_mute
; /* unused */ charline_mix_gain
; /* 0..31 line mix to output gain in -1.5dB steps */ charline_mix_mute
; /* non-zero mutes line mix */ chardac_attn
; /* 0..61 dac attenuation, in -1.5 dB steps */ chardac_mute
; /* non-zero mutes dac output */ }; typedef struct sound_setup { struct channelleft
; /* left channel setup */ struct channelright
; /* right channel setup */ enum sample_ratesample_rate
; /* sample rate */ enum sample_formatplayback_format
; /* ignore (always 16bit-linear) */ enum sample_formatcapture_format
; /* ignore (always 16bit-linear) */ chardither_enable
; /* non-zero enables dither on 16 => 8 bit */ charmic_attn
; /* 0..64 mic input level */ charmic_enable
; /* non-zero enables mic input */ charoutput_boost
; /* ignore (always on) */ charhighpass_enable
; /* ignore (always on) */ charmono_gain
; /* 0..64 mono speaker gain */ charmono_mute
; /* non-zero mutes speaker */ } sound_setup;
On PPC systems the mic_attn
and mic_enable
parameters are used to
control the amount of adc to dac "loopback" instead of the microphone
input level.
The SOUND_LOCK_FOR_DMA
ioctl takes an
(audio_buffer_header*) argument but
can be ignored except on Macintosh. A Macintosh driver should call
lock_memory()
on the audio buffer with the
B_DMA_IO
flag.
The audio server opens the driver named
/dev/old/sound
. So when you
have implemented these ioctl calls and you want to test your driver with
the audio server, you can either name it
/dev/old/sound
or name it
something like /dev/old/mydriver
and create a symbolic link in your
UserBootscript
file:
ln -s /dev/old/mydriver /dev/old/sound
The "old" in this path name is to remind you that this is an interface which will be deprecated in the future.
In the previous article on DynaDraw, we built a no-frills app—a basic window and a view, and our drawing algorithm. In this article, we get to the fun part—adding lots of controls which modify how the calligraphy strokes are drawn.
Download the code from:
ftp://ftp.be.com/pub/samples/interface_kit/obsolete/dynadraw2.zip,
compile it, and see how the new features work before getting into the details of this article. I especially dig wireframe mode, which calls StrokePolygon instead of FillPolygon.
Let's start out the way we did last time, looking at what we'd like to do, and what we'll need to do it:
First, we'd like a menu bar across the top of the window. Under it, we'll put two menus:
and . Under , we'll have the items , which brings up an About Box, and , which, well, quits. Under , we'll have three items: , which brings up a panel for controlling the mass, drag, and other parameters; , which allows us to change the color of the pen; and , which clears our drawing.
The Tweakables panel and the Color panel will each have their own
windows. R3 introduces a new window type, B_FLOATING_WINDOW
, which is
perfect for control windows. Floating windows have smaller borders and
tabs than regular windows, and they are active whenever the application
is active.
The next question is: which object should be responsible for managing
these control windows? It's possible to add these duties to the DDWindow
class, but if our application grows to include more windows, this
approach will be cumbersome. A better solution is to subclass
BApplication
in an object which manages the creation and destruction of
the control windows.
Here's our object list now:
FilterView
, from the first article,
DDWindow
, from the first article,
TweakWin
, which controls pen parameters,
ColorWin
, which controls pen color, and
DDApp
, which manages the application and windows.
Good object layout is half the battle, and the other half is good
messaging. The heart of this entire application lies in the
MessageReceived()
functions in each of the classes listed above. Reading
those functions will tell you what duties those objects perform, so let's
go over them now.
Our application object handles B_ABOUT_REQUESTED
, and
B_QUIT_REQUESTED
, which is standard. It also handles
TWEAK_REQ
and COLOR_REQ
, which
are requests to open the tweakables window and the color window. These
messages (or more properly, commands, because they are messages which
contain no data) come from DDWindow
, which sends
them in response to the user selecting the appropriate menu item. Our
application object also handles TWEAK_QUIT
and
COLOR_QUIT
, messages sent from the tweakables window
and from the color window the user has closed them.
ColorWin
has only one specific message to deal with:
COLOR_CHG
, which is a message we requested the
BColorControl
send us when the user has chosen a new
color. (This is specified in the BColorControl
constructor.) In this case, we want to alert the
DDWindow
of the new color selection, so we pack the
RGB value of the user's selection into the message:
caseCOLOR_CHG
: /* get the current color settings, */ /* attach to the message */ /* and pass it along to the handler */ rgb_colorclr
=cc
->ValueAsColor
();swatch
->SetViewColor
(clr
);swatch
->Invalidate
();msg
->AddInt16
("red",clr
.red
);msg
->AddInt16
("green",clr
.green
);msg
->AddInt16
("blue",clr
.blue
);handler
->PostMessage
(msg
); break;
So our message is really getting double-duty: it serves as a command from
the BColorControl
object to
ColorWin
indicating a color change, and it
serves as a message (with the RGB value as data) from
ColorWin
to DDWindow
.
TweakWin
's MessageReceived()
is more of the same. It handles MASS_CHG
,
DRAG_CHG
, WIDTH_CHG
, and
SLEEPAGE_CHG
, which arrive from the BSlider
s
created in TweakWin
's constructor. Again, an
int32 value is added to each
of these messages, and the message is then sent to the DDWindow
object.
TweakWin
also generates FILL_CHG
and ANGLE_CHG
messages, >from the Fill
and Angle check boxes. We don't need to add any data to these messages,
so we can send them directly on to the target, DDWindow
, rather than
having TweakWin
::MessageReceived()
pass the message along to DDWindow
:
fill
= newBCheckBox
(BRect
(25,320,135,340), "fill", "Wireframe", newBMessage
(FILL_CHG
));fill
->SetTarget
(handler
); /* send FILL_CHG directly to handler */
SetTarget()
is a function defined in
BInvoker
, which is a subclass of
BCheckBox
.
This technique is also used in the menus of DDWindow
. Since we want
B_ABOUT_REQUESTED
and B_QUIT_REQUESTED
to be sent along to the
application object, we can use a shortcut function, SetTargetForItems()
,
which will send the messages of all menu items in the
menu to be_app
:
BMenu
*menu
= newBMenu
("File");menu
->AddItem
(newBMenuItem
("About", newBMessage
(B_ABOUT_REQUESTED
)));menu
->AddItem
(newBMenuItem
("Quit", newBMessage
(B_QUIT_REQUESTED
))); /* Both the About and Quit messages should be directed to be_app */menu
->SetTargetForItems
(be_app
);mb
->AddItem
(menu
);
In the TWEAK_REQ
and COLOR_REQ
to go to
be_app
, we want CLEAR_SCREEN
to
post to DDWindow
, so we specify the
targets one at a time:
menu
= newBMenu
("Controls");BMenuItem
*tmpItem
= newBMenuItem
("Tweakables", newBMessage
(TWEAK_REQ
));tmpItem
->SetTarget
(be_app
);menu
->AddItem
(tmpItem
);tmpItem
= newBMenuItem
("Color", newBMessage
(COLOR_REQ
));tmpItem
->SetTarget
(be_app
);menu
->AddItem
(tmpItem
);menu
->AddSeparatorItem
();menu
->AddItem
(newBMenuItem
("Clear Screen", newBMessage
(CLEAR_SCREEN
))); /* Target not specified for Clear, so the target will be DDWindow */mb
->AddItem
(menu
);AddChild
(mb
);
Notice that in DDWindow
::MessageReceived()
, we
simply pass all of the messages from the tweakables window and the color
window to the FilterView
, which actually interprets
them. Note how values are extracted from messages in
FilterView
's
MessageReceived()
:
caseCOLOR_CHG
: int16red
,green
,blue
;msg
->FindInt16
("red", &red
);msg
->FindInt16
("green", &green
);msg
->FindInt16
("blue", &blue
);SetHighColor
(red
,green
,blue
); break;
R3 introduces the BSlider
class, an incredibly useful control device.
It's really flexible too, allowing you to customize the look of the
thumb, the tick marks, the track, and more. Since BSlider
only deals with
integers, you may need to do some small tricks to get the value you want,
such as setting up a slider with a range of values between 0 and 100, and
using this as a percentage of your maximum value. This is what we're
doing with Mass, Drag, and Width.
Also, BSlider
s are set up to have the left hand side be the minimum
value, and the right hand side as the maximum value. Sometimes this isn't
what you want to do: in the case of "Sleepage", it seemed more natural to
me to "sleep less" towards the right of the control, and "sleep more"
towards the left.
In this case, just set the slider up to go from your minimum to your
maximum, and when reading that value, subtract it >from your maximum, and
add that value to your minimum. In the sleep case, I set up the slider to
go from 3000 microseconds to 30000 microseconds, and then (in
FilterView
::MessageReceived()
)
I subtract the slider value from 30000 (the
maximum), and add the difference to 3000 (the minimum).
This program can be extended in a variety of ways, and I encourage you to
do so. Some small projects might include letting the user control the
background color of the FilterView
, and dynamic updating of the color
swatch. Larger projects include saving the drawing to disk, printing, and
letting the user undo brush strokes. Experiment and enjoy!
We've "heard" this on e-mail and from newsgroups. We're likely to hear it again at PC Expo, so I thought I'd take a few minutes to clarify our position. The contradiction astute observers point out is this: On the one hand we present ourselves as a media OS, a specialized platform for audio and video applications. We sometimes use the "A/V Linux" sound byte -- and then proceed to show off "non-A/V" applications on our site and, soon, at our booth. Come on, they say, you're really trying to be a general purpose OS—you're competing with Microsoft, you just don't want to admit it!
Let's address the "non-A/V" applications first. We believe the "tractor apps" will come from the field of audio and video activities—editing, synthesizing, producing, and mangling digital media. However, this belief doesn't imply that the BeOS can't or shouldn't be put to other uses.
Imagine for a moment that you're a software developer and all you care about is word processing. Your mother said you shouldn't, but you do, either to aggravate her or just because it's in your soul. Let's further assume you want to make money at it, presumably to show dear old mom who's right. Are you going to try writing a Windows word processor? Of course not. Microsoft Office is the monarch and you'll never impress your mother.
On the BeOS though, the adventure might look risky, but not suicidal. And you can allay family scepticism by telling them about the advantages of Electronic Software Distribution (ESD) and how BeDepot.com is for software authors without knee pads.
"Non-A/V" applications for BeOS have another advantage: users will be happier with their BeOS system, even if their primary use is the "official" media driven one. They still want word processing and e-mail.
Further, imagine a page layout program on the BeOS. It's likely to be livelier, faster, more reactive, and more available to the user than its venerable elders are on the Mac or Windows now. The better feel of the program, its real-time WYSIWYG behavior comes from the programmer, of course. But it also makes a good case for what the BeOS platform has to offer: a good combination of multithreading and—on the right hardware -- multiprocessing, fast graphics, and good APIs.
But, keen observers will insist, this makes you a general-purpose OS, non? Therefore, you're competing with Microsoft and, therefore, you're dead—you just don't realize it yet.
Ah, but this is sophistry. Yes, computers are programmable, in fact they can all be simulated by a simple Turing machine. As a result, all hardware and all system software can do anything. All true enough, if we don't consider how much time, aggravation, or money is involved. So, in the general-purpose office productivity field, I don't believe anyone can beat Microsoft for the time being. Windows, with or without Internet Explorer, Office, Money... your everyday needs are well covered and OEM relationships are such that no new entrants can survive.
But, if Windows can "do" audio and video, it doesn't do it very well. And Windows 98 shows little sign of anything but marginal improvements on what Windows 95 offered. That's where we complement Windows, and the fact that Be developers offer a broader range of applications doesn't make us a competitor to Microsoft Office. In other words, just as Windows can "do" audio and video but doesn't shine there, we can "do" more than A/V, but that's not where we will win.
So, our priority continues to be the A/V domain, where we are most useful. And, when we like a page layout program or an e-mail client on the BeOS, that sentiment doesn't change the nature of our platform.
BeDevTalk is an unmonitored discussion group in which technical information is shared by Be developers and interested parties. In this column, we summarize some of the active threads, listed by their subject lines as they appear, verbatim, in the mail.
To subscribe to BeDevTalk, visit the mailing list page on our web site: http://www.be.com/aboutbe/mailinglists.html.
Multiple inheritance questions:
Wendell Beckwith has noticed that you can't set a BTextControl
's text
view object to be uneditable until after the control has been added to
its window. In other words, this is no good:
control
->TextView
()->MakeEditable
(false
);window
->AddChild
(control
); /* The text view is still edible. */
Why should this be? Peter Potrebic suggests that instead of setting the text view's editable state, you should simply set the control to be disabled. But, responded Mr. Beckwith, disabling the control affects the control's label (it turns gray). Why should the two states (text editability and label appearance) be tied together?
From Sander Stoks, a question and a plea:
“I searched the FAQ, searched BeGeek.com, tried everything. How does one
make (use of) a shared lib on R3/Intel? [And] _please_ don't tell me that
I will have to add __declspec()
stuff to all that code...”
Jon Watte responded to Mr. Stoks question, but couldn't satisfy the no-declspec request:
“The easiest fix is to have a file Exports.h
which __declspec()
-s all
your classes, and include this file first in each and every one of your
header files. You also need to __declspec(dllimport)
the same entities
that you previously exported when you get around to using the shared
library.
Also, you need to include the init_term_dyn.o
file among your libraries
for static initialization to happen correctly.”
Chris Herborth seconded the spirit of Mr. Stoks' letter:
“Just fix the damn development tools. >}:-( There should be NO DIFFERENCE when building an app/library/shared library for BeOS, whether you're on PowerPC or x86.”
There was a groundswell of support for the position, which is, in essence, that Be should concentrate more on the performance, robustness, and portability of the development tools. Fred Fish gave a reasoned response to the escalating criticism:
“Although I'm not thrilled about the current tools situation with all the 'windows cruft', I've learned to hold my nose and just get on with work... I'm still hoping that someday we will have the opportunity to either hide this stuff better or dump it entirely. Whether that point is months from now or a few years down the road, I think it will eventually happen.”
So how should Be go about "transitioning" to new tools? Wendell Beckwith says drop the hammer:
“If Be ... should redraw the line for backwards compatibility and adopt a single object/executable file format that works across all of their architectures, then I believe the users/developers would rather bite the bullet now than wait until later when Be software is more numerous and doing something like this will be much harder to pull off.”
The thread concentrated, thereafter, on itchy symbol exportation/library inclusion details. For more info on the topic, see "Project Libraries" and "Building a Library" in http://www.be.com/documentation/rel_notes/R3DevLayout.html
Does the BeOS have any "Wizard" API? (According to Mark B. Elrod, a Wizard is a “dialog which guides you through a process...It is most often used for installers and things that need to gather info.”)
You can, of course, build a Wizard-like UI yourself from windows, textviews, buttons and bows. But should there be a specialized panel class that knows how to draw and apply back/forward labels to successive "slides"? Factotum Jon Watte rushed out and wrote one, damn it: ftp://ftp.b500.com/pub/BeOS/wizard.zip
It's dead. But, as Jon Watte pointed out:
“You can use templates for the same effect. Or just make the rule that every class...should have a private typedef of inherited:”
classMyClass
: publicBView
{ public: private: typedefBView
inherited; };
“Making it private means it won't clash with further subclasses of MyClass.”