Issue 2-37, September 17, 1997

Be Engineering Insights: Introducing the BSynth Class

By Marc Ferguson

One of the new features in the Preview Release is the software MIDI synthesizer. The Midi Kit contains three classes for controlling the MIDI synthesizer: BSynth, BMidiSynth, and BMidiSynthFile. The BSynth object is the interface to the underlying synthesizer engine. BMidiSynth is a subclass of BMidi that forwards MIDI events to the BSynth object. And BMidiSynthFile is a specialization of BMidiSynth which reads from a standard MIDI file.

An application that uses the MIDI synthesizer starts by creating a BSynth object. Currently you can only create one BSynth object per application, but you probably only need one anyway. After creating the BSynth, call LoadSynthData(B_BIG_SYNTH) on it. This tells the BSynth to use the instrument file /boot/beos/etc/synth/big_synth.sy.

If the call to LoadSynthData() fails, it probably couldn't find the instrument file. LoadSynthData() does not load the instruments into memory yet. Unless you are playing directly from a MIDI file you will still need to load the instruments.

The application then creates a BMidiSynth object which you can send MIDI events to just as you would to any other BMidi object. After creating the BMidiSynth, call EnableInput() on it. The first argument to EnableInput() should be true, and the second argument indicates whether to pre-load all instruments from the instrument file.

The easiest way to see how this works is to try it. Here is a program that plays six notes on a piano. Don't forget to link with libmidi.so.

#include <MidiSynth.h>

main ()
{
  BSynth synth;
  status_t err = synth.LoadSynthData(B_BIG_SYNTH);
  if (err) {
    printf ("LoadSynthData: %x\n", err);
    return -1;
  }

  BMidiSynth song;
  err = song.EnableInput(true, false);
  if (err) {
    printf ("EnableInput: %x\n", err);
    return -1;
  }

  int channel = 1;       // MIDI channel number
  int instrument = B_ACOUSTIC_GRAND; // GM instrument
  song.LoadInstrument(instrument);
  song.ProgramChange(channel, instrument);

  for (int i = 0; i < 6; i++) {
    song.NoteOn(channel, 60 + 5 * i, 127);
    snooze(250000);
  }

  snooze(2000000);
  song.AllNotesOff(FALSE);
  return 0;
}

In this example channel is a MIDI channel number (1-16), and instrument is a General MIDI instrument number. The instrument numbers are represented by the midi_axe constants defined in MidiDefs.h. The ProgramChange() call associates an instrument with a channel.

You can also play the synthesizer from an external MIDI keyboard. If you are on a Mac you will also need a Mac MIDI interface (I'm using an "Opcode Midi Translator II"). To do this, you just connect a BMidiPort to the BMidiSynth:

BSynth synth;
synth.LoadSynthData(B_BIG_SYNTH);

BMidiSynth song;
printf("Loading instruments...\n");
song.EnableInput(true, true);
printf("Done.\n");

BMidiPort port;
status_t err = port.Open("midi2"); // printer port on Macs
if (err)
  printf ("Open: %x\n", err);
port.Connect(&song);
port.Start();

In this case the second argument to EnableInput() is true. So all the instruments will be pre-loaded, which may take a few seconds. Note that on Macs, "midi1" is the modem port and "midi2" is the printer port.

To play a MIDI file, use a BMidiSynthFile instead of a BMidiSynth:

#include <MidiSynthFile.h>

main ()
{
  BSynth synth;
  synth.LoadSynthData(B_BIG_SYNTH);

  char* filename = "/boot/optional/midi/BeBoxBeBop.mid";
  BMidiSynthFile song;
  entry_ref ref;
  get_ref_for_path(filename, &ref);
  status_t err = song.LoadFile(&ref);
  if (err) {
    printf ("LoadFile: can't open %s\n", filename);
    return -1;
  }

  song.Start();
  while (!song.IsFinished())
    snooze(1000000);
  song.Fade();
  return 0;
}

There are also several other BSynth methods such as SetReverb() and SetSynthVolume() to experiment with, but this is enough to get started using the BSynth. So dig out those old MIDI keyboards and plug them in!


A Problem with Extensions

By Baron Arnold

Inspiration comes wheeling around the corner like a '68 Chevy Camero: screaming rubber, hoodless, with a caved-in passenger door. Greek blue and pumped to evade, or catch, or surprise.

Effort squeezes into a tiny hallway, sweaty and breathing hard. Then comes the split, the lesson, the book, the movie and you wanna know the rest...

Buy the rights.
How bizarre.

I spent 35 minutes waiting for my friend's 9500/132 to upgrade itself from 7.5.something to 7.6. Then Corel's word processor would lock up whenever she tried to save a document. Neat.

So she called Corel and they told her it was $25 for help.
Neat.

Tonight she told me that it turned out to be a problem with Extensions. Imagine that. A problem with Extensions. You could almost write a recent-history book called...

"A Problem with Extensions"

And you could write it with StyledEdit, and you could look up the words you don't know how to spell with NetPositive targeted to autoload your favorite online dictionary. You could even e-mail the final manuscript to your publisher, click back and check your bank account to make sure they don't stiff you on the five cents a word you're paid to describe the very disappointing behaviour of one careless twit behind that big fruity curtain.

Because baby you had it all along.
BeOS CDs are breeding like AOL floppies.

She asked if the BeOS would save her, and I said, "Yes." It's simple, really.

She's a student. She wants e-mail, a word processor, and Internet access.
She doesn't want a million upgrade headaches.
She doesn't need all those fancy legacy applications.
She's got nice hardware.
Calgon, take me away.

PR2 is almost ready.
It fixes most of what you were asking for.

Oh and those of you who have been submitting PPP bugs, please remember to tell us what kind of modem you have and if possible, what kind of modem you are connecting to.

So blah blah blah Power Computing and Apple, and blah blah blah CHRP and BURP (Be United Reference Platform.) Blah blah blah Moto and does it really matter anyway? Latitude is coming. Chill out for a couple months. Then it's gonna get VERY interesting. You saw the demo.

I'll see a thousand BIntel machines clog my cube before Xmas, and by Spring you'd be a fool not to be running multi-boot on your 95% of the back seat.

If you kids don't stop fighting I'll turn this IPO around right now!

And speaking of super-duper, if anyone out there at Radius is reading this, then we've got a couple of bugs with those kool expensive cards you sell. Achieve greater market penetration, contribute to the Be Hardware Fund. Pack up your dreams of compatibility and ship them to:

Baron Arnold
800 El Camino Real, Suite 300
Menlo Park, CA 94025.

Oh and props out to the people who built the Be Machine.
I use it as my main testing box.
It's fast and blue. =)
I love the overspray on the CD drawer the most.
Very kool.

But seriously, about the Be Hardware Fund, if you are somehow in charge of something where what you make is something you'd like to see BeCompatible, send me one please?

And for those of you just tuning in, the Intel world is just so chock full of hardware options, send me stuff. Really. You know the song. We'll try support it, but only if we can test it.

Speaking of testing, I wanna publicly thank Jake Hambey. He's filed a million good bugs and helped me take a few very big steps into scripting the testing grunt work by porting Expect. If you ever wanted to script a telnet session then get Expect from BeWare.

Wow. Thanks, Jake.
And ya know?
He plays a mean ocarina. =)

Driving up to the Avenues the other night, I was telling Jake that it's really the people at Be who have allowed us to develop at such a rapid rate. It has been the inter-linking of so many different personalities, of so many different work styles: so many concessions and acknowledgments made in the name of The Right Way. It's quite simple, and yet, terrifically effective. We joked with abstraction about the BeActionPlaySet, plastic StarWars-size BePlay figures of Dominic, and Benoit, and George. And we considered, with respect, how many more of each individual engineer Be would need to cover perfectly the near-future work to be done. As for testing, I think just one each of Ming and Mel and Jake and I could handle it. Well, maybe two Mings. One for each wing. =) We'll definitely need more space though.

And so the fourth floor acquisition is but one release away (gotta put those engineers somewhere). MIA and CBS and DVD and I'm a little worried that we will grow too fast. That there will be so much to do that we won't have time to find out who our co-workers are. What they love and dislike. What they have seen. Where they have been. Hmmm. Perhaps this is an attempt to jinx us. Keep the lid on our secret a little longer. Let this GUI simmer in these, the last of our simple days.

There are so many things we'd like to give you.
We talk about them every day.
At lunch, at dinner, on the weekends.
We want to give you everything.

So blah blah blah MacWorld and Comdex.
Blah blah blah shop talk shop talk shop talk.
Watch for BeShred in a shrink wrap near you.
Get your ticket now for "The Power of the Word NO."
Hang on to your sense of humor and consider please
that 150%, is just a sleepier 110.

Spin a few times, but never forever.
Try to be humble and honest and clever.
Love = work + your life and your friends so Valerie,
here's where my article ends.


News From The Front

By William Adams

Are you AllAttached? When you're using a programming framework, sometimes it takes a while to figure out all the ins and outs of what happens when. The BeOS programming interface is clean and simple to use, but learning a little bit about proper usage can go a long way. I was recently playing with a library of code which was largely unknown to me, when my application kept crashing on initialization. It turns out that I was trying to set a target handler for a object that didn't quite exist yet. Specifically it was a button in one of the views that is not created until the AttachedToWindow() method is called.

Well, AttachedToWindow() gets called when you actually add a view to a window's hierarchy. At this point, you can call the Window() method of a view to get a pointer to the window you're attached to. Also, at this point none of your child view's AttachedToWindow() methods will have been called, and you know nothing about your sibling views. So what's safe to do here? Things that are directly related to your view that you didn't already do in your constructor. You might add child views, set your font and colors and all that.

What about AllAttached()? You want to use this when it is absolutely paramount that all of your child views have been attached. This would be the case if you are going to perform an action that depends on the existence of your child views in the hierarchy.

So let's say there should be a button in your view with the name "OK". And you want to set the target handler of that button to be yourself. Ideally you want to do the following:

BButton *aButton = (BButton *)FindView("OK");
if (0 != aButton)
  aButton->SetTarget(this);

Where is it safe to put this code? Well, if you were the view writer and you know explicitly when the button was created, then you can do it anywhere you want. If you are sub-classing some other view, then it's safest to stick the code in the AllAttached() method, assuming that the original view author created the button in their AttachedToWindow() method.

This is what I did, and my bugs went away. Just goes to show you...

Then I got to figuring, there's got to be more uses of BMessageFilters. So I sought to simplify the HelloWorld application. Often times when you create a simple little app, you might create a sub-class of BApplication and BWindow, simply to implement the BWindow::QuitRequested() method so when the window closes, the app will quit. Well, that seems like a bit much just to quit an app, how about a filter?

#include <MessageFilter.h>
#include <Application.h>
#include <Window.h>

static filter_result
QuitFilter(BMessage *msg, BHandler **target,
           BMessageFilter *filter)
{
  be_app->PostMessage(B_QUIT_REQUESTED);
  return B_SKIP_MESSAGE;
}

In this case, whenever this filter receives a message, it will post a message to the application object asking it to quit. You could have actually checked the *msg passed in to make sure what type of message it is, but in this very simple case we don't care. As you will see below, the filter is installed in such a way that only one type of message will come to this filter in the first place.

So, now my simple application to pop a window up on the screen and quit when it's closed looks like this:

BRect gWindowSize = BRect(100,80,260,120);

class HelloView : public BView {
public:
    HelloView(BRect frame, char *name);
virtual  void  Draw(BRect updateRect);
};

HelloView::HelloView(BRect rect, char *name)
        : BView(rect, name, B_FOLLOW_ALL, B_WILL_DRAW)
{
}

void HelloView::Draw(BRect)
{
  SetFont(be_bold_font);
  SetFontSize(24);

  MovePenTo(BPoint(10, 30));
  DrawString("Hello, World!");
}

main()
{
  // Start off with an instance of an application object
  BApplication *myApplication = new BApplication('HLWD');

  // Setup our window
  BWindow *aWindow = new BWindow(gWindowSize, "Hello",
    B_TITLED_WINDOW, B_NOT_RESIZABLE);

  // Create a filter that will only look at B_QUIT_REQUESTED
  // message. These are sent to the window whenever the
  // close box is clicked on, or when you do an Alt-Q.
  BMessageFilter *filter =
    new BMessageFilter(B_QUIT_REQUESTED, QuitFilter);
  aWindow->AddCommonFilter(filter);

  // view rect should be same size as window rect but with
  // left top at (0, 0)
  gWindowSize.OffsetTo(B_ORIGIN);
  HelloView *aView =
    new HelloView(gWindowSize, "HelloView");

  // add view to window
  aWindow->AddChild(aView);

  // make window visible
  aWindow->Show();

  // Start the application running
  myApplication->Run();

  // After it is finished running, delete it
  // to be nice and tidy
  delete(myApplication);
  return(0);
}

This is a very simple app. All it needs is a specialized view to be added to the window and you're all set. You can use this as a template for knocking out those quick and dirty apps where you just want to try something out in a window real quick.

If all your view does is draw something, you could conceivably create a simple base class view that you could install a drawing function into. It would then in turn call this drawing function whenever the Draw() method was called.

Fun things can be done with this. If you haven't already started playing with filters, then maybe this gives you some incentive. They really are flexible and allow you to quickly and dynamically change the nature of your application simply by installing and removing filters.

How can filters be used for an interface builder? Maybe that's what someone can do next?


The Photoshop Machine

By Jean-Louis Gassée

CHRP had the right goal: To create a hardware standard around the PowerPC similar in intent to what IBM's PC/AT became for the Intel processor, the core platform around which a huge industry was built. Almost forgotten is that while IBM designed the PC/AT, it lost control of the standard. It was IBM's very loss of control that gave birth to the clone industry, now driven, if not controlled, by Intel and Microsoft.

This bit of history isn't lost on Apple's management. They remember IBM's futile attempt to regain control of the PC standard with the proprietary PS/2 platform and have concluded that even with a hypothetical "private" version of CHRP, they would lose the hardware battle. In many respects, CHRP would have been a bigger danger to Apple than Power Computing ever was: Power's engineers were industrious and talented—had CHRP lived, industry and talent wouldn't have been necessary. Lower the bar and increase the competition.

Regardless of what we think of Apple's decision to bring the Mac cloning experiment to an end, the demise of CHRP is a logical consequence of the de-cloning move. Yet, many have stated their opinion that there is life left in CHRP—potentially. As a result, we've seen several suggestions on the 'Net with a view to breathing commercial life back into an "independent" PowerPC platform.

These observers go on to suggest there must be a considerable inventory of CHRP systems or, if not systems, ready-to-assemble components otherwise condemned to the scrap heap. The performance of CHRP hardware demonstrated by Motorola and IBM is enticing; why not use cost-competitive, if not "devalued" CHRP hardware to build a BeOS-based "Photoshop box"?

This is an engaging suggestion, and the thought experiment it triggers will allows us to explore issues such as dedicated systems vs. general purpose ones, the cost and performance benefits of the BeOS for a given application space and porting existing applications vs. enabling new ones.

If I follow the dominoes correctly, Photoshop has a large user base, these users are hungry for performance, or cost savings, or both, and many would buy one or more additional "boxes" just to run Photoshop if they saw enough productivity gains in a dedicated machine. A dual processor CHRP system just running a BeOS version of Photoshop would excite a large enough user population to incite one or more hardware manufacturers to ship such a product. In turn, this would constitute a sizeable enough business to trigger Adobe into making Photoshop available for this system.

This, needless to say, is purely hypothetical and I don't want to imply that any of the companies mentioned or alluded to have expressed the slightest shadow of interest for the idea. This is purely a thought experiment.

Even with this stipulation, I won't speculate too much as to why Adobe would or wouldn't consider such an idea. Performance is always welcome by Photoshop users and we've demonstrated substantial improvements on existing hardware as well as on affordable multi-processor systems.

Still, if I dare to imagine Adobe's concerns, there must be the cost of producing a new version of Photoshop and there must be the alternative of the Intel space. In the latter, there is inexpensive dual-processor hardware and interesting performance improvements in the making. I don't know what would happen if Adobe would let it be known they'd support the concept of a dedicated Photoshop system. Where would the best bids come from?

In the past, dedicated systems haven't fared too well. Take the word processor. In the early difficult days of the Mac, many observers remarked on the ease of use and beautiful printing obtained with MacWrite. They concluded the Mac should be reconfigured as a dedicated word processor. That didn't happen. Does this apply to the current idea? On the one hand customers have shown a liking for a system with a wide range of actual or potential uses. On the other hand, word processing applications are not driven by the thirst for performance Photoshop users experience.

Addressing cost, using the BeOS in a dedicated configuration might do more than add performance. Using the modularity of the BeOS and targeting Photoshop users could further reduce the footprint of the system and thus save hardware dollars. Still, others will remind us that hardware ought to stay as close as possible to the mainstream in order to best capture the benefits of competition between vendors.

Lastly, there is the question of new software versus mature applications. On this issue, the assumptions of today's thought experiment matter a great deal. Historically, ports from one generation of operating systems to a new one don't do well. The reasons might be that the technical constraints of the old design don't permit effective use of the new platform, or it could be that the press of business on the existing front leaves no financial or psychic energy for a risky bet on a new vehicle. (DOS to Windows, or Apple II to Mac are changes of generation; Mac to Windows isn't; Mac or Windows to the BeOS are.)

What differs in today's thought experiment is the assumption that a dedicated box would be sold almost exclusively to existing Photoshop users who want the simplest, least disruptive, most effective way of improving their existing work flow. In such a context a port could do very well.

It is too early to tell if the many suggestions we've seen on the 'Net for a "Photoshop box" will lead to an actual implementation, but we certainly appreciate the positive sentiments they convey.

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