<< Newer Articles posted February 2005 Older >>

MAME(tm)

Suffice to say, everyone who works on MAME is well aware of the attempted theft of our name and logo. Appropriate, measured steps are being taken to solve this issue. This is not something that is going to be helped by a lot of public hand-wringing by those involved with MAME, so don't expect a lot of public comments on the matter. Just trust that it's being taken care of, and if help is needed, it will be asked.

On the compile warpath

Got all the FM chips converted over, and finally decided to move forward with just compiling everything and fixing up problems as the compiler finds them. (Rule #1 of making a huge system-wide change like this: be absolutely sure that anything you don't change causes a compile error so that you don't forget fixing it!)

I can compile successfully up to sndhrdw/qix.c. Still quite a ways to go, but it's getting easier as things move on.

Once it compiles, of course, I have the fun work of making sure that a decent sampling of titles still play sound correctly before I hand it off to be version 0.93. Then the fun becomes everyone else's. :-)

Chipping away….

The bulk driver conversion is well underway now. Unfortunately, it is a very manual procedure. In order to keep things from getting too confusing, I'm approaching it one sound chip at a time.

I first start by scanning all the files in MAME for a particular sound interface structure, say AY8910interface. Then I visit each instance of that interface, examine how it is used, and fix it up to the new specs. This involves visiting all the machine drivers that reference that sound interface and modifying the MDRV_SOUND_ADD macros to specify the clock. Then I have to add the speaker routing, which specifies how each output from the sound chip maps to a speaker, including the gain (volume). Once I've done that, I remove the clock and volume from the original sound interface.

One additional thing I have done is changed the main sndintrf.h include file so it doesn't include every single sound driver header. This means that for each driver I visit, I have to add an explicit #include at the top for each sound chip it uses. This is tedious, but should help the overall compile speed of MAME.

So far, I have completed all the AY8910, YM2203, YM2151, YM2608, YM2610, YM2612 and DAC interfaces. I am currently in the midst of doing the OKIM6295 ADPCM chip. After that, I will move on to less commonly used sound chips. Eventually, I will have fixed up every place in the code and then I get to try making it all compile. With luck, I may finish before next weekend....

Filters and streams

The old MAME sound design came about as a result of an evolution from the original MAME code. The problem with evolutionary coding is that at some point it gets gross and unmanageable. This is exactly what happened with the existing sound core. The flow of sound is essentially:

   sound 
		core -> stream -> mixer -> OSD
This isn't really so bad, except that streams were something we added later, so a lot of old code is still left directly talking to the mixer. On top of that bit of fun, some basic RC filters were added to the streams engine, and some other filter types were added to the mixer engine.

In the new order of things, as I said before, there is no separate mixer engine. In addition, I have enhanced the functionality of a stream. Originally, a stream was a single output channel of audio. Chips that output stereo, for example, would end up allocating a special "joined stream", which was really two streams, but which act kind of like a single stream. Confused yet? ;-)

Anyway, the new streams are more flexible. Each stream can have multiple output channels, as well as multiple input channels. Each input channel can be connected to a single output from another stream. And each output channel can be connected to multiple input channels on other streams. Here are some examples.

Two chips with a single output each, routed to a mono speaker. In this case the mixer is created with two inputs and one output:

   chip #1 
		---+
+---> mixer filter ---> mono speaker
chip #2 ---+

One mono chip with a single output, routed to both the left and right speakers. In this case we have two speaker outputs, and two mixers, each with one input. The output from the mono chip is split to the input of two filters:

           
		+---> left mixer filter ---> left speaker
chip ---+
+---> right mixer filter ---> right speaker

Now the creation of filters becomes dead easy. The RC filter that is in the stream engine now just becomes another sound component that you need to wire up. Let's say you want an RC filter on the output of one of the chips in the first example. It's just:

   chip #1 ---> RC filter 
		--+
+---> mixer filter ---> mono speaker
chip #2 -----------------+

And the one other nice thing is that with a few minor tweaks to the discrete sound engine, it can also make use of these input sound streams in order to do much more complex and accurate filtering than was possible with the hacked-in basic filters.

Speed and the new sound system

Some folks have been asking how the sound system rewrite will affect speed. The answer is, yes, it will make things a bit slower. Probably not substantially enough to make a difference unless you're right on the edge, performance-wise. To understand why things will be slower, let's take a look at what happens with, say, Pac-Man sound in MAME today, versus what it will be in the future.

Today, the sequence of events for generating a chunk of sound in MAME looks like this:


  1. The Namco sound core generates samples at its internal frequency into a stream buffer.

  2. The mixer core resamples the stream buffer, applies gain, and sums the results into a mixer buffer.

  3. The mixer core finally clamps the mixer buffer to 16-bit dynamic range and combines the left/right buffers into a single stream.

  4. That stream is fed to the OSD layer.

In the new system, because of some abstraction layers I've introduced, the steps are a bit longer. Conceptually, sound flows in a graph from the sound cores through optional filters and into a final mixing stage. The mixer is no longer an integral component; rather, it acts as a filter with multiple inputs and a single output. The graph for Pac-Man is simple:

   Namco sound -> mixer filter -> "mono" speaker

Since there is only one speaker on Pac-Man, there is only a single final output. Let's look at what happens internally:


  1. The Namco sound core generates samples at its internal frequency into a stream buffer.

  2. The stream system notices that the stream buffer from the Namco core is necessary for the mono speaker output. In response, it resamples the stream buffer and applies a gain, placing the result in a resampling buffer.

  3. The mixer filter adds all of its inputs togther (using the resampling buffer data) and generates its output in a stream buffer.

  4. The sound core system then loops over all the speakers (one in this case), takes the stream buffer from the associated mixer, and adds the contents to either the left channel, right channel, or both channels, depending on the position of the speaker.

  5. The sound core finally clamps the mixer buffer to 16-bit dynamic range and combines the left/right buffers into a single stream.

  6. That stream is fed to the OSD layer.

Now, there are some basic optimizations that can be done. In step (2), if the gain is 1.0, and the input sampling rate matches the sampling rate that is needed by the mixer, then we don't need to resample at all, so that can potentially save us a step.

Step (4) also will eventually go away, and we will hand each speaker's stream to the OSD layer so that it can be output with appropriate 3D positioning. I don't plan to do this anytime in the near future, but it's a straightforward extension of the system I am putting together.

A couple of other things I am doing will slow down the system a bit. For one thing, I am getting rid of "mono" sound output. If a game is mono, it will output identical left/right data streams, but there is little point in keeping the added complexity of supporting mono output at the OSD layer.

The other slowdown is that when the input sample rate is higher than the output sample rate at a resampling point, I intend to do a more accurate "sum of energy" calculation instead of a simple linear interpolation or even simpler crude resampling. This should help reduce aliasing effects when the sound cores operate at a higher frequency than your sound card, but it will be more expensive.

One additional slowdown I am considering is using 32-bit intermediate buffers for all the sound mixing. This allows us to more easily "overdrive" the sound and downmix into 16-bit only at the end, rather than clipping at each stage. Accessing the extra memory will cost at the cache level, but on the plus side, we can get rid of a lot of intermediate code that does clipping along the way and just do it in the final step.

Next post I'll talk a little bit about filters and how they fit into the mix, so to speak.