Description of Sound???.CC
This file is intended for programmers who would like to port the sound
output routines to their platform. It gives a short outline what services
have to be provided.
You should also have a look at the exisiting files, SOUNDLNX.CC
for Linux and SOUNDWIN.CC for Windows.
Overview
Include file
The include file is "bochs.h". It contains all definitions for
the system-independent functions that the SB16 emulation uses.
Functions
The following functions should be supplied by this output driver, even
if only as stubs. They should return BX_SB16_OUTPUT_OK (0)
if the function was successful, and BX_SB16_OUTPUT_ERR (1) if
not. Note that the return information is not used at the moment.
General functions:
Midi functions:
Wave functions:
Variables
The following variable is defined in the BX_SB16_C class and is available
to the output functions:
Details
Functions
int bx_sb16_c::initoutput()
initoutput() is called at the start of the emulator. It should
do the following tasks:
-
If necessary, allocate memory for and initialize the outputdata
structure.
-
Should any part of the initialization fail, output should be disabled by
setting
bx_options.sb16.midimode = 0;
bx_options.sb16.wavemode = 0;
If appropriate, only midi or wave output can be disabled.
It should not do the following:
-
Allocate the output devices. This shouldn't be done until the actual output
occurs; in either initmidioutput() or initwaveoutput().
Otherwise it would be impossible to have two copies of Bochs running concurrently.
int bx_sb16_c::doneoutput()
doneoutput() is called just before Bochs ends. It should
-
free the memory used by the outputdata structure (if any)
int bx_sb16_c::openmidioutput(char *device)
openmidioutput() is called when the first midi output starts.
It is only called if the midi output mode is 1 (midimode 1). It should
-
prepare the given MIDI hardware for receiving midi commands.
openmidioutput() will always be called before openwaveoutput(),
and closemidioutput()will always be called before closewaveoutput(),
but not in all cases will both functions be called.
Description of the parameters:
-
device is a system-dependent variable.
It contains the value of the MIDI=device configuration option.
Note that only one midi output device will be used at any one time.
device
may not have the same value throughout one session, but it will be closed
before it is changed.
int bx_sb16_c::sendmidicommand(int delta,
int command, int length,
Bit8u data[])
sendmidicommand()is called whenever a complete midi command has
been written to the emulator. It should then
-
send the given midi command to the midi hardware.
It will only be called after the midi output has been opened.
Description of the parameters:
-
delta is the number of delta ticks that
have passed since the last command has been issued. It is always zero for
the first command. There are 24 delta ticks per quarter, and 120 quarters
per minute, thus 48 delta ticks per second.
-
command is the midi command byte (sometimes
called status byte), in the usual range of 0x80..0xff. For more information
please see the midi standard specification.
-
length is the number of data bytes that
contained in the data structure. This does not include the status
byte. It can only be greater than 3 for SysEx messages (0xF0 and
0xF7)
-
data[] is the array of these data bytes.
Note, it might be NULL if length==0.
int bx_sb16_c::closemidioutput()
closemidioutput() is called before shutting down Bochs or the
emulator after it gets the stop_output command. After this, no
more output will be necessary until openmidioutput() is called
again. It should
-
Reset and close the midi output device
int bx_sb16_c::openwaveoutput(char *device)
openwaveoutput() is called when the first wave output occurs,
and only if the selected wavemode is 1. It should
-
Open the given device, and prepare it for wave output
or
-
Store the device name in the outputdata structure, so that it
can be opened in startplayback().
openmidioutput() will always be called before openwaveoutput(),
and closemidioutput()will always be called before closewaveoutput(),
but not in all cases will both functions be called.
openwaveoutput() will typically be called once, where startplayback()
is called for every new DMA transfer to the SB16 emulation. If feasible,
it could be useful to open and/or lock the output device in startplayback()
to ensure that it can be used by other applications while Bochs doesn't
need it.
The parameters are
-
device is the wave device selected by
the user. It is strictly system-dependent. The value is that of the
WAVE=device
configuration option.
Note that only one wave output device will be used at any one time.
device
may not have the same value throughout one session, but it will be closed
before it is changed.
int bx_sb16_c::startwaveplayback(int
frequency,
int bits, int
stereo, int
format)
This function is called whenever the application starts a new DMA transfer.
It should
-
Open the wave output device, unless openwaveoutput() did that
already
-
Prepare the device for data and set the device parameters to those given
in the function call
The parameters are:
-
frequency is the desired frequency of the
output. Because of the capabities of the SB16, it can have any value between
5000 and 44,100.
-
bits is either 8 or 16, denoting the resolution
of one sample.
-
stereo is either 1 for stereo output,
or 0 for monaural output.
-
format is a bit-coded value (see below).
The bits in format are
Bit number |
Meaning |
0 (LSB) |
0: unsigned data
1: signed data |
1..6 |
Type of codec |
7 |
0: no reference byte
1: with reference byte |
8..x |
reserved (0) |
|
|
The codec can be one of the following:
Value |
Meaning |
0 |
raw data |
1 |
reserved |
2 |
2-bit ADPCM
(Creative Labs format) |
3 |
2.4-bit (3-bit) ADPCM
(Creative Labs format) |
4 |
4-bit ADPCM
(Creative Labs format) |
Other codecs are not supported by the SB hardware. In fact, most applications
will translate their data into raw data, so that in most cases the codec
will be zero. |
The number of bytes per sample can be calculated as (bits / 8) * (stereo
+ 1).
I recommend that these values be stored in the outputdata structure
if they are going to be used in the future. Although they can be accessed
through the emulator variables as well, this is not recommended.
int bx_sb16_c::sendwavepacket(int length,
Bit8u data[])
This functions is called whenever a data packet of at most BX_SB16_WAVEPACKETSIZE
has been sent to the SB16 emulator. It should then
-
Send this wave packet to the wave hardware
This function has to be synchronous, meaning that it has
to return immediately, and not wait until the output is done. Also,
this function might be called before the previous output is done. If your
hardware can't append the new output to the old one, you will have to implement
this yourself, or the output will be very chunky, with as much silence
between the blocks as the blocks take to play.
Parameters:
-
length is the number of data bytes in
the data stream. It will not be larger than BX_SB16_WAVEPACKETSIZE.
-
data is the array of data bytes.
The order of bytes in the data stream is the same as that in the
Wave file format:
Output type |
Sequence of data bytes |
8 bit mono |
Sample 1; Sample 2; Sample 3; etc. |
8 bit stereo |
Sample 1, Channel 0; Sample 1, Channel 1; Sample 2, Channel 0; Sample
2, Channel 1; etc. |
16 bit mono |
Sample 1, LSB; Sample 1, MSB; Sample 2, LSB; Sample 2, MSB; etc. |
16 bit stereo |
Sample 1, LSB, Channel 0; Sample 1, MSB, Channel 0; Sample 1, LSB,
Channel 1; Sample 1, MSB, Channel 1; etc. |
Typically 8 bit data will be unsigned with values from 0 to 255, and
16 bit data will be signed with values from -32768 to 32767, although the
SB16 is not limited to this. For further information on the codecs and
the use of reference bytes please refer to the Creative Labs Sound Blaster
Programmer's Manual, which can be downloaded from the Creative Labs web
site.
int bx_sb16_c::stopwaveplayback()
This function is called at the end of a DMA transfer. It should
-
Close the output device if it was opened by startwaveplayback().
int bx_sb16_c::closewaveoutput()
This function is called just before Bochs exits. It should
-
Close the output device, if this hasn't been done by stopwaveplayback()
and free any memory used by the wave output functions.
Typically, stopwaveplayback() will be called several times, whenever
a DMA transfer is done, where closewaveoutput() will only be called
once. However, in the future it might be possible that openwaveoutput()
is called again, for example if the user chose to switch devices while
Bochs was running. This is not supported at the moment, but might be in
the future.
Variables
void *outputdata
This variable is unused by the emulator itself and is intended to hold
data for the output part. It will be initialized to NULL at the
start of the emulator and should be initialized by the initoutput()
function. outputdata can be type cast to any struct you like.
It will not be touched by the emulator; it is only used in the output file.
I recommend the definition of a structure (just local to the output
file) and a #define to access it like this:
struct bx_sb16_output_struct {
// put the variables used by your functions here
};
#define OUTPUTDATA ( (bx_sb16_output_struct*) (BX_SB16_THIS outputdata)
)
Then the variables can be accessed by OUTPUTDATA->variablename.
Since this is rather difficult to debug, it might make sense to define
a global variable and have OUTPUTDATA point to it while debugging
the output functions.
Boolean waveoutputready
This variable tells the emulator if the wave output functions are ready
to receive a data block. It has to be handled by the output functions,
whenever output is running, i. e. between a startwaveplayback
and a stopwaveplayback call. If it is not set when a block of
data becomes available, this block is not sent until waveoutputready
is set to 1. Note that the time between setting it and the block
being sent to the output function is approximately the time for one byte
(or word in 16bit DMA) to be transferred, but in reality may be slightly
longer.
It should be accessed by the following construct
BX_SB16_THIS waveoutputready
Boolean midioutputready
This is equivalent to waveoutputready, except that it is for midi
output.
It should be accessed by
BX_SB16_THIS midioutputready
Other variables
All other variables should be read-only, but the compiler doesn't ensure
this. I don't think it's necessary to use any of them and that they should
rather be passed to the individual functions as parameters. If you need
the value of a variable and it's not passed as parameter, please tell me
and I will add it to the parameter list.