Skip to content

Commit

Permalink
readme touchups
Browse files Browse the repository at this point in the history
  • Loading branch information
mjrgh committed Jun 25, 2023
1 parent 5eba00f commit ae92d8b
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 49 deletions.
8 changes: 5 additions & 3 deletions DCSDecoder/Doc/DCS_format_reference.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,16 @@ <h1>DCS Audio Format Technical Reference</h1>
audio recordings that can be used with the DCS hardware.
</p>
<p>
This documentation is part of my DCS Explorer project, which
This documentation is part of my
<a href="https://github.com/mjrgh/DCSExplorer" target="_blank">DCS Explorer</a>
project, which
includes a portable C++ decoder/player that understands all of the DCS
audio formats, and an encoder that can create new, original DCS audio ROMs,
including new compressed digital audio streams in the DCS format.
In the course of that project, I had to figure out
how DCS works at a detailed level. I've attempted to capture what
I learned here, for sharing and future reference. The DCS Explorer
project might be useful as a companion to this material, as a
I learned here, for sharing and future reference. DCS Explorer's
C++ source code might be useful as a companion to this material, as a
reference implementation of the algorithms described here.
</p>
<p>
Expand Down
80 changes: 53 additions & 27 deletions DCSDecoder/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# DCSDecoder Class

DCSDecoder is a C++ class that decodes and plays back the audio in a
pinball ROM set for a game that used the DCS audio system.
pinball ROM set for a game that used the DCS audio system. It's
designed as a reusable, stand-alone class that can easily be
embedded in any C++ project, with no external dependencies.


## How is this different from PinMame's DCS emulator?
Expand All @@ -16,41 +18,65 @@ implementation of a compatible decoder. It's an entirely stand-alone
class, with no dependencies on any outside projects, that can be
easily embedded in any C++ program. It also features a relatively
simple programming interface that lets you perform DCS decoding and
playback with a few function calls.
playback with a few function calls. It's designed to let the host
program drive the audio playback system timing; it just serves as
a source of PCM samples, which you can retrieve on your own schedule.

There are several benefits to this approach. One is that C++ code is
much easier to read than ADSP-2105 assembly code (even if you already
know how to read ADSP-2105 assembly), so this implementation can serve
as an educational resource for those wishing to learn about how the
DCS format works. Another is that the native implementation is
There are several benefits to this approach. One is that it lets
you see what the decoder is actually doing inside. PinMame by design
doesn't concern itself with what's going on inside the software; it
tackles the emulation problem by mimicking the original hardware.
You could always look at the original ADSP-2105 machine code to
see what it was doing, but PinMame doesn't provide much in the way
of tools for that, and even if it did, ADSP-2105 assembly code doesn't
make for easy reading (for me, at least) and doesn't do much to
illuminate what's going on at any level of abstraction above bits
and bytes. So an immediate benefit to a C++ implementation is that
it's a lot easier to see what going on at the design level.

Another benefit is that the native implementation is
considerably faster than an emulator, by a factor of at least 20.
This makes it possible to use the native version on processors that
would be too slow to run the emulator for real-time playback, such as
microcontrollers. A third benefit is that the native class is easy to
would be too slow to run the PinMame ADSP-2105 emulation, such as
microcontrollers.

A third benefit is that the native class is easy to
embed in host applications. It has no external dependencies (apart
from the standard C++ run-time libraries) and is designed for easy use
in just about any application architecture. The PinMame emulator, in
contrast, is so entangled with PinMame's internal interfaces and
structures that it's impractical to reuse in almost any other context.
in just about any application architecture. The PinMame DCS emulator,
in contrast, is so entangled with PinMame's internal application structure
that it's impractical to reuse in almost any other context.


## Alternative emulator decoder

DCSDecoder *also* features a subclass that implements decoding through
the ADSP-2105 emulator, much like PinMame. But that's really a bonus
add-on, and isn't used at all when running the native decoder. What's
more, it's just as stand-alone as the native decoder: you can embed it
in a C++ application using the identical abstract class interface used
with the native decoder.

The main reason the emulator version is part of the project at all is
to serve as a reference implementation to test that the native decoder
is working properly. The emulated version is as close as we can get
on a PC to the actual DCS hardware. That's useful because it would be
difficult to test rigorously against physical DCS boards. The board
don't have any way to read the bit stream going into the DAC, and even
if they did, it would be hard to synchronize that with the data on the
PC side.
DCSDecoder is structured into a base class that provides an abstract
interface to the decoder, and subclasses that implement specific
decoding strategies. So far, we've been talking about the native
decoder implementation, which is a ground-up, 100% C++ implementation
of the DCS decoder and run-time system.

The project contains a second subclass that implements decoding
through ADSP-2105 machine code interpretation, much like PinMame.
This version of the decoder simply runs the original ROM code in
emulation. Note that this subclass *still* doesn't somehow
incorporate PinMame - it uses the same ADSP-2105 machine code
interpreter that PinMame does, but it doesn't share anything
with PinMame outside of that. The emulator subclass is just as
easy to embed in a new C++ project as the native decoder is.

The emulator decoder isn't integral to the project - it's a bonus
feature. You can run the native decoder without even including the
emulator files in the build; the emulator is just a parallel
subclass specialization. The main reason the emulator version is
part of the project at all is to serve as a reference implementation
to test that the native decoder is working properly. The emulated
version is as close as we can get on a PC to the actual DCS hardware,
so it serves as our reference point for correct behavior. (It would
be better still to test against a physical DCS board from a pinball
machine, but that would take some pretty specialized equipment. It's
not within the realm of the practical for me. Testing against the
emulator is the next best thing.)


## Using the decoder class in a program
Expand Down
15 changes: 9 additions & 6 deletions DCSEncoder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,14 @@ patch for PinMame).
for stream files. You can specify this as many times as necessary
to add multiple directories to search.

Note that DCS ROM sizes must be 512K or 1M. (The Wikipedia page on
DCS incorrectly states that DCS-95 boards could accept 2M ROMs. A
close look at the schematics shows that they're actually limited to
the same 1M limit as the original boards. Sorry to disappoint if you
were hoping to create a mod with a gigantic amount of new material.)
Note that DCS ROM sizes must be 512K or 1M. The Wikipedia page on
DCS notes that DCS-95 boards could accept 2M ROMs, but the schematics
suggest that this capability was optionally enabled or disabled at the
factory via a soldered jumper. None of the DCS pinball ROMs ever
exceeded 1M, so I suspect that all of the boards shipped with pinball
machines were factory-configured for 1M chips only. My assumption
is that 2M ROMs would fail if installed in a physical DCS board,
so the encoder doesn't allow that as an option.


## Output file
Expand Down Expand Up @@ -1049,7 +1052,7 @@ everything is set back to normal at the end.

<indent>
Note: this command is only allowed in the 1994 and later
software.
software.
</indent>

<indent>
Expand Down
9 changes: 9 additions & 0 deletions DCSEncoderTester/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# DCS Encoder Tester

This is a small sub-project to assist with testing the DCS Encoder class.
It implements an interactive command-line program that lets you convert
audio files to the DCS stream format and play them back to test the
results.

This tester program doesn't create DCS ROMs. See the main DCSEncoder
project for that.
25 changes: 12 additions & 13 deletions DCSExplorer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ interactively play back its audio tracks.
DCS sound board (without the pinball machine that goes with it). On
the original machines, the pinball controller commanded the sound
board by sending it the track numbers to play. The interactive mode
works the same way, letting you type in the track numbers. Each track
works the same way, letting you play the role of the WPC host board
by typing in the command numbers manually. Each track
number is a four-digit hexadecimal number; use the `--tracks` option
to get a listing of the available tracks.

Expand Down Expand Up @@ -156,29 +157,27 @@ beats.

The track design is the root reason that it's always been so difficult
to extract all of the audio in a DCS ROM for conversion to another
format. It's simply not possible in principle to extract *everything*,
because the content is inherently dynamic.
format. The content is inherently dynamic, so merely trying all
of the track numbers one at a time doesn't necessarily tell you
everything that's in the ROM.

A "stream" contains the actual audio, as compressed digital data.
DCS streams use a proprietary lossy compression format based on the
same basic DSP math as more widely used formats like MP3 and Vorbis.

I'll document the stream format in detail separately. The quick
overview is that a stream is s sequence of frames, where a frame
The full details of the stream format can be found in my
[DCS Audio Format Technical Reference](http://mjrnet.org/pinscape/dcsref/DCS_format_reference.html).
The quick overview is that a stream is s sequence of frames, where a frame
represents a 7.68ms time window, containing 240 consecutive PCM
samples at 31250 samples per second. The binary data stream doesn't
contain the PCM samples directly; instead, a stream stores a
frequency-domain transformation of the PCM data over a 256-sample time
window (256, not 240, because consecutive frames overlap by 16
samples). The transformed samples are then stored in the stream using
a ZIP-like bit-compression encoding that tries to squeeze the bits
into a smaller space by using short bit sequences in the coding to
represent common bit patterns in the data, similar to how Morse Code
assigns the shortest dot-and-dash patterns to the most frequently used
letters. That part of the coding is lossless, but it only accounts
for a small part of the compression. Most of the compression comes
from the "lossy" part, which saves space by reducing the precision of
the stored information. For example, if an audio sample in the
a Zip-like bit-compression encoding. That part of the coding is lossless,
but it only accounts for a small part of the compression. Most of the
compression comes from the "lossy" part, which saves space by reducing the
precision of the stored information. For example, if an audio sample in the
original frequency-domain frame has the value 1.234567, the encoder
can save some space by rounding that to 1.235, 1.24, or even just 1.2,
depending on how the sample relates to other samples within the frame.
Expand Down

0 comments on commit ae92d8b

Please sign in to comment.