You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2166 lines
36 KiB

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML
><HEAD
><TITLE
>The sound lowlevel interface</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
REL="HOME"
TITLE="Bochs Developers Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="About the code"
HREF="about-the-code.html"><LINK
REL="PREVIOUS"
TITLE="Sound Blaster 16 Emulation"
HREF="sb16-emulation-basics.html"><LINK
REL="NEXT"
TITLE="Harddisk Images based on redologs"
HREF="harddisk-redologs.html"></HEAD
><BODY
CLASS="SECTION"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>Bochs Developers Guide</TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="sb16-emulation-basics.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 2. About the code</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="harddisk-redologs.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="SECTION"
><H1
CLASS="SECTION"
><A
NAME="SOUND-LOWLOVEL-BASICS"
>2.9. The sound lowlevel interface</A
></H1
><P
>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.</P
><P
>You should also have a look at the exisiting files, <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>SOUNDLOW.CC</I
></SPAN
>,
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>SOUNDMOD.CC</I
></SPAN
> and e.g. <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>SOUNDLNX.CC</I
></SPAN
> for Linux
or <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>SOUNDWIN.CC</I
></SPAN
> for Windows and their respective header files
to get an idea about how these things really work.</P
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="AEN476"
>2.9.1. Files</A
></H2
><P
>The main include file for a lowlevel sound driver is <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>iodev.h</I
></SPAN
>.
It has all definitions for the system-independent functions that a sound driver
uses. The sound driver also needs to include <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>soundlow.h</I
></SPAN
> for
the definitions of the base classes <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_sound_lowlevel_c</I
></SPAN
>,
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundlow_waveout_c</I
></SPAN
>, <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundlow_wavein_c</I
></SPAN
>
and <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundlow_midiout_c</I
></SPAN
>.</P
><P
>Additionally, every output driver will have an include file, which should be
included on top of <TT
CLASS="FILENAME"
>soundmod.cc</TT
> to allow the emulator
to use that driver. The code to initialize the object for the selected drivers
can be found in that file, so a soundcard emulation does not need to include
the specific driver headers.</P
><P
>To actually make the emulator use any specific driver as the default,
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>BX_SOUND_LOWLEVEL_NAME</I
></SPAN
> has to be set to the name of the
respective driver.</P
><P
>Note that if your class contains any system-specific statements,
include-files and so on, you should enclose both the include-file and
the CC-file in an <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>#if defined</I
></SPAN
> (OS-define) construct.
Also don't forget to add your file to the list of lowlevel sound object
files (<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>SOUNDLOW_OBJS</I
></SPAN
>) in the file <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>configure.in</I
></SPAN
>
and to regenerate the configure script,</P
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="AEN493"
>2.9.2. Defines and strutures</A
></H2
><P
><PRE
CLASS="SCREEN"
>#define BX_SOUNDLOW_WAVEPACKETSIZE 19200
#define BX_SOUNDLOW_OK 0
#define BX_SOUNDLOW_ERR 1
typedef struct {
Bit16u samplerate;
Bit8u bits;
Bit8u channels;
Bit8u format;
Bit16u volume;
} bx_pcm_param_t;
const bx_pcm_param_t default_pcm_param = {44100, 16, 2, 1};</PRE
></P
><P
>The maximum size of a wave data packet, the return values of the lowlevel
functions, the structure for the PCM parameters and the default parameter
set are also important for the sound driver development. They can be found
in the main include file <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>soundlow.h</I
></SPAN
>.</P
><P
>All lowlevel sound methods called from the device code have to return either
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>BX_SOUNDLOW_OK</I
></SPAN
> if the function was successful, or
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>BX_SOUNDLOW_ERR</I
></SPAN
> if not. If any of the initialization
functions fail, the device emulation should disable the affected feature.</P
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="AEN502"
>2.9.3. Classes</A
></H2
><P
>The following classes are involved with the sound lowlevel interface:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundmod_ctl_c</I
></SPAN
> is a pseudo device that is used to
initialize the sound drivers depending on the configuration.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_sound_lowlevel_c</I
></SPAN
> is the base class of the
lowlevel sound support. It has methods to return pointers to the objects for
the available services <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>waveout</I
></SPAN
>, <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>wavein</I
></SPAN
>
and <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>midiout</I
></SPAN
>. The base class returns NULL for all services.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_sound_dummy_c</I
></SPAN
> is derived from <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_sound_lowlevel_c</I
></SPAN
>.
It returns vaild pointers for all services, but the output classes are only
implemented as stubs and the <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>wavein</I
></SPAN
> service returns silence.
This "dummy" driver is used whenever a OS specific driver does not implement
all services.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundlow_waveout_c</I
></SPAN
>, <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundlow_wavein_c</I
></SPAN
>
and <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundlow_midiout_c</I
></SPAN
> are the base classes for the
services provided by the Bochs lowlevel sound support. Some methods are stubs
and used by the "dummy" sound driver, others are helper methods and used by
the OS specific implementations derived from these base classes.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_sound_OS_c</I
></SPAN
> is derived from <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_sound_lowlevel_c</I
></SPAN
>.
It returns vaild pointers for all services it implements for the selected
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>OS</I
></SPAN
> (operating system / library) or NULL for services it does
not implement. In the second case the Bochs sound init code falls back to the
"dummy" driver.</P
></LI
></UL
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="AEN530"
>2.9.4. The base class <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_sound_lowlevel_c</I
></SPAN
></A
></H2
><P
><PRE
CLASS="SCREEN"
>class bx_sound_lowlevel_c : public logfunctions {
public:
bx_sound_lowlevel_c();
virtual ~bx_sound_lowlevel_c();
virtual bx_soundlow_waveout_c* get_waveout() {return NULL;}
virtual bx_soundlow_wavein_c* get_wavein() {return NULL;}
virtual bx_soundlow_midiout_c* get_midiout() {return NULL;}
protected:
bx_soundlow_waveout_c *waveout;
bx_soundlow_wavein_c *wavein;
bx_soundlow_midiout_c *midiout;
};</PRE
></P
><P
>The base class for sound lowlevel support is derived from the <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>logfunctions</I
></SPAN
>
class to make the Bochs logging capabilities available in the sound driver code.
The constructor of this base class only initializes all pointers to NULL and
the destructor deletes the objects if necessary.</P
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="AEN537"
>2.9.5. The <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>waveout</I
></SPAN
> base class <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundlow_waveout_c</I
></SPAN
></A
></H2
><P
><PRE
CLASS="SCREEN"
>class bx_soundlow_waveout_c : public logfunctions {
public:
bx_soundlow_waveout_c();
virtual ~bx_soundlow_waveout_c();
virtual int openwaveoutput(const char *wavedev);
virtual int set_pcm_params(bx_pcm_param_t *param);
virtual int sendwavepacket(int length, Bit8u data[], bx_pcm_param_t *src_param);
virtual int get_packetsize();
virtual int output(int length, Bit8u data[]);
virtual int closewaveoutput();
virtual int register_wave_callback(void *, get_wave_cb_t wd_cb);
virtual void unregister_wave_callback(int callback_id);
virtual bx_bool mixer_common(Bit8u *buffer, int len);
protected:
void convert_pcm_data(Bit8u *src, int srcsize, Bit8u *dst, int dstsize, bx_pcm_param_t *param);
void start_mixer_thread(void);
bx_pcm_param_t emu_pcm_param, real_pcm_param;
int cvt_mult;
int cb_count;
struct {
void *device;
get_wave_cb_t cb;
} get_wave[BX_MAX_WAVE_CALLBACKS];
int pcm_callback_id;
};</PRE
></P
><P
>The base class for wave output support is also derived from the
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>logfunctions</I
></SPAN
> class. In addition to wave output methods
used from sound devices, it contains everything required for the mixer thread
feature (register PCM sources, convert data formats, start mixer).</P
><P
>The constructor should <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>not</I
></SPAN
> allocate the output devices.
This should be done in <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
>.</P
><P
>This table shows the waveout class methods, where are they called from and
if a platform / library specific implementation is required.
<DIV
CLASS="TABLE"
><A
NAME="AEN549"
></A
><P
><B
>Table 2-4. Waveout methods</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL><COL><COL><THEAD
><TR
><TH
>Method</TH
><TH
>Called from</TH
><TH
>Platform code</TH
></TR
></THEAD
><TBODY
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
></TD
><TD
>Sound init code</TD
><TD
>Required</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>set_pcm_params()</I
></SPAN
></TD
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
> and <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>sendwavepacket()</I
></SPAN
></TD
><TD
>Required</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>sendwavepacket()</I
></SPAN
></TD
><TD
>Sound device emulation</TD
><TD
>Optional</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>get_packetsize()</I
></SPAN
></TD
><TD
>Mixer thread</TD
><TD
>Optional</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>output()</I
></SPAN
></TD
><TD
>Mixer thread</TD
><TD
>Required</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>closewaveoutput()</I
></SPAN
></TD
><TD
>Sound device emulation</TD
><TD
>Optional</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>register_wave_callback()</I
></SPAN
></TD
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
> and sound device emulation</TD
><TD
>Optional</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>unregister_wave_callback()</I
></SPAN
></TD
><TD
>class destructor and sound device emulation</TD
><TD
>Optional</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>mixer_common()</I
></SPAN
></TD
><TD
>Mixer thread</TD
><TD
>Optional</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>convert_pcm_data()</I
></SPAN
></TD
><TD
>Internal</TD
><TD
>No</TD
></TR
><TR
><TD
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>start_mixer_thread()</I
></SPAN
></TD
><TD
>Internal</TD
><TD
>No</TD
></TR
></TBODY
></TABLE
></DIV
>&#13;</P
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN616"
>2.9.5.1. int openwaveoutput(const char *wavedev)</A
></H3
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
> is called when the sound output subsystem
initializes. It should do the following:</P
><P
></P
><UL
><LI
><P
>Set up the default PCM parameters for output.</P
></LI
><LI
><P
>Open the given device, and prepare it for wave output.</P
></LI
><LI
><P
>Register the callback function for the PCM buffer queue (<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>sendwavepacket()</I
></SPAN
>
adds the output to the queue and the mixer thread gets it from there).</P
></LI
><LI
><P
>Start the mixer thread, unless the sound library has it's own one (e.g. SDL).</P
></LI
></UL
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
> will only be called once, whereas
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>set_pcm_params()</I
></SPAN
> is called whenever the PCM samplerate
has been changed.</P
><P
>The parameters are the following:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>wavedev</I
></SPAN
> is the wave output device selected by the user.
It is strictly system-dependent. Some sound libraries currently ignore this
value and use the default one instead. The value is that of the <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>waveout=device</I
></SPAN
>
configuration parameter of the <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>sound</I
></SPAN
> bochsrc option.</P
></LI
></UL
><P
>Note that only one wave output device will be used at any one time.
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>wavedev</I
></SPAN
> may not have the same value throughout one session,
but it will be closed before it is changed.</P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN642"
>2.9.5.2. int set_pcm_params(bx_pcm_param_t *param)</A
></H3
><P
>This function should called from <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
> to initialize
the output device with the default parameters and from <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>sendwavepacket()</I
></SPAN
>
whenever the samplerate has been changed in the emulated sound device.
It should do the following:</P
><P
></P
><UL
><LI
><P
>Open the wave output device, unless <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
> did that
already.</P
></LI
><LI
><P
>Prepare the device for data and set the device parameters to those given
in the function call.</P
></LI
></UL
><P
>The parameters are the following:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>param</I
></SPAN
> is a pointer to a structure containing the set of
parameters required to set up a sound device for PCM output.</P
></LI
></UL
><P
>The members of the structure <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_pcm_param_t</I
></SPAN
> are these:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>samplerate</I
></SPAN
> is the desired frequency of the
output. Because of the capabities of the soundcards, it can have any value
between 5000 and 48,000.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bits</I
></SPAN
> is either 8 or 16, denoting the resolution
of one sample.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>channels</I
></SPAN
> is the number of channels (2 for stereo output,
or 1 for mono output.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>format</I
></SPAN
> is a bit-coded value (see below).</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>volume</I
></SPAN
> is the output volume to be used by the mixer code.
The 16 bit value consists of two 8 bit values for each channel.</P
></LI
></UL
><P
><DIV
CLASS="TABLE"
><A
NAME="AEN677"
></A
><P
><B
>Table 2-5. format bits</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL><COL><THEAD
><TR
><TH
>Bit number</TH
><TH
>Meaning</TH
></TR
></THEAD
><TBODY
><TR
><TD
> 0 (LSB) </TD
><TD
><P
> 0: unsigned data </P
><P
> 1: signed data </P
></TD
></TR
><TR
><TD
> 1..6 </TD
><TD
> Type of codec (see below) </TD
></TR
><TR
><TD
> 7 </TD
><TD
><P
> 0: no reference byte </P
><P
> 1: with reference byte </P
></TD
></TR
><TR
><TD
> 8..x </TD
><TD
> reserved (0) </TD
></TR
></TBODY
></TABLE
></DIV
>
<DIV
CLASS="TABLE"
><A
NAME="AEN701"
></A
><P
><B
>Table 2-6. codecs</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL><COL><THEAD
><TR
><TH
>Value</TH
><TH
>Meaning</TH
></TR
></THEAD
><TBODY
><TR
><TD
> 0 </TD
><TD
> PCM (raw data) </TD
></TR
><TR
><TD
> 1 </TD
><TD
> reserved </TD
></TR
><TR
><TD
> 2 </TD
><TD
> 2-bit ADPCM (Creative Labs format) </TD
></TR
><TR
><TD
> 3 </TD
><TD
> 2.4-bit (3-bit) ADPCM (Creative Labs format) </TD
></TR
><TR
><TD
> 4 </TD
><TD
> 4-bit ADPCM (Creative Labs format) </TD
></TR
></TBODY
></TABLE
></DIV
></P
><P
>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.</P
><P
>The number of bytes per sample can be calculated from this as (bits / 8) * channels.</P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN726"
>2.9.5.3. int sendwavepacket(int length, Bit8u data[], bx_pcm_param_t *src_param)</A
></H3
><P
>This function is called whenever a data packet of at most
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>BX_SOUNDLOW_WAVEPACKETSIZE</I
></SPAN
> is ready at the soundcard
emulation. It should then do the following:</P
><P
></P
><UL
><LI
><P
>Add this wave packet to the waveout buffer chain after converting to 16 bit signed
little endian. If the samplerate has been changed <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>set_pcm_params()</I
></SPAN
>
should be called to update the sound hardware settings.</P
></LI
></UL
><P
>Parameters:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>length</I
></SPAN
> is the number of data bytes in
the data stream. It will never be larger than <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>BX_SOUNDLOW_WAVEPACKETSIZE</I
></SPAN
>.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>data</I
></SPAN
> is the array of data bytes.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>src_param</I
></SPAN
> is a pointer to a structure containing the PCM parameters
(see above).</P
></LI
></UL
><P
>The order of bytes in the data stream is the same as that in the Wave file format:
<DIV
CLASS="TABLE"
><A
NAME="AEN747"
></A
><P
><B
>Table 2-7. wave output types</B
></P
><TABLE
BORDER="1"
CLASS="CALSTABLE"
><COL><COL><THEAD
><TR
><TH
>Output type</TH
><TH
>Sequence of data bytes</TH
></TR
></THEAD
><TBODY
><TR
><TD
> 8 bit mono </TD
><TD
> Sample 1; Sample 2; Sample 3; etc. </TD
></TR
><TR
><TD
> 8 bit stereo </TD
><TD
> Sample 1, Channel 0; Sample 1, Channel 1; Sample 2, Channel 0; Sample 2, Channel 1; etc. </TD
></TR
><TR
><TD
> 16 bit mono </TD
><TD
> Sample 1, LSB; Sample 1, MSB; Sample 2, LSB; Sample 2, MSB; etc. </TD
></TR
><TR
><TD
> 16 bit stereo </TD
><TD
> Sample 1, LSB, Channel 0; Sample 1, MSB, Channel 0; Sample 1, LSB, Channel 1; Sample 1, MSB, Channel 1; etc. </TD
></TR
></TBODY
></TABLE
></DIV
></P
><P
>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
soundcard emulations are not limited to this.
site.</P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN768"
>2.9.5.4. int get_packetsize()</A
></H3
><P
>This function is called from the mixer thread to retrieve the size of a wave data
packet based on the current samplerate. By default the packet size is big enough
to send output for 0.1 seconds. If the host sound driver / library uses a different
value, this value should be returned with this method.</P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN771"
>2.9.5.5. int output(int length, Bit8u data[])</A
></H3
><P
>This function is called from the mixer thread to send the mixed PCM output to
the host sound hardware.</P
><P
>Parameters:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>length</I
></SPAN
> is the number of data bytes in
the data stream. It will never be larger than the value returned from <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>get_packetsize</I
></SPAN
>.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>data</I
></SPAN
> is the array of data bytes.</P
></LI
></UL
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN783"
>2.9.5.6. int closewaveoutput()</A
></H3
><P
>This function is currently only called from the soundcard emulation if the "file"
driver is used. This makes the runtime change of the output file possible.
By default this method does nothing and the wave output device is closed in the
destructor of the specific class.</P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN786"
>2.9.5.7. int register_wave_callback(void *arg, get_wave_cb_t wd_cb)</A
></H3
><P
>This function is called from <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
> to register
the function to retrieve data from the PCM output buffer chain. Other sound
emulation devices (e.g. OPL3, PC speaker) can register a function to poll the
data from the device emulation. The return value is the ID of the registered
function and it is usually used to unregister the source.</P
><P
>Parameters:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>arg</I
></SPAN
> is the pointer to the device emulation object.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>wd_cb</I
></SPAN
> is the pointer to a static function that returns
wave data from the device emulation. This function is usually called from the
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>mixer_common()</I
></SPAN
> method.</P
></LI
></UL
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN799"
>2.9.5.8. void unregister_wave_callback(int callback_id)</A
></H3
><P
>This function is usually called from the destructor of the sound emulation
device to unregister it's registered function to poll PCM data. If the
driver / library doesn't use the default mixer thread, a specific implementation
of this method my be required.</P
><P
>Parameter:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>callback_id</I
></SPAN
> is the ID of the function to unregister.</P
></LI
></UL
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN807"
>2.9.5.9. bx_bool mixer_common(Bit8u *buffer, int len)</A
></H3
><P
>This is the main wave output mixing function. It is called from the mixer
thread, it polls the wave data from all registered sources and it mixes the
data using a simple algorithm (addition and clipping). The return value
indicates whether or not wave data is available for output.</P
><P
>Parameters:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>buffer</I
></SPAN
> is the output buffer for the wave data.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>len</I
></SPAN
> is the maximum length of the output buffer.</P
></LI
></UL
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN818"
>2.9.5.10. void convert_pcm_data(Bit8u *src, int srcsize, Bit8u *dst, int dstsize, bx_pcm_param_t *param)</A
></H3
><P
>This function converts the PCM data sent from the sound device emulation to the
16 bit stereo signed little endian format. It should be called in <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>sendwavepacket()</I
></SPAN
>
after allocating the output buffer in the buffer queue. Future versions might
also perform resampling here.</P
><P
>Parameters:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>src</I
></SPAN
> is the buffer containing data sent from the sound emulation.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>srcsize</I
></SPAN
> is the amount of wave data to be converted.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>dst</I
></SPAN
> is the buffer for the converted wave data.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>dstsize</I
></SPAN
> is the size of the destination buffer.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>param</I
></SPAN
> is a pointer to the struture containing the format
parameters of the source data.</P
></LI
></UL
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN839"
>2.9.5.11. void start_mixer_thread()</A
></H3
><P
>This function starts the mixer thread and it should be called in <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveoutput()</I
></SPAN
>
unless the sound driver / library has it's own way to do this (e.g. SDL). This
function also initializes the mutex required for locking the mixer thread when
adding data to the buffer chain or unregistering a source.</P
></DIV
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="AEN843"
>2.9.6. The <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>wavein</I
></SPAN
> base class <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundlow_wavein_c</I
></SPAN
></A
></H2
><P
><PRE
CLASS="SCREEN"
>class bx_soundlow_wavein_c : public logfunctions {
public:
bx_soundlow_wavein_c();
virtual ~bx_soundlow_wavein_c();
virtual int openwaveinput(const char *wavedev, sound_record_handler_t rh);
virtual int startwaverecord(bx_pcm_param_t *param);
virtual int getwavepacket(int length, Bit8u data[]);
virtual int stopwaverecord();
static void record_timer_handler(void *);
void record_timer(void);
protected:
int record_timer_index;
int record_packet_size;
sound_record_handler_t record_handler;
};</PRE
></P
><P
>The base class for wave input support is also derived from the
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>logfunctions</I
></SPAN
> class. It contains the framework for wave
input (recording) support. The base class is used by the "dummy" sound driver
and returns silence to let the input mechanism of the soundcard emulation work.
The soundcard emulator object needs to implement a callback function to notifies
the emulation about available data. This function usually calls the driver method
to get the wave data packet. The driver objects has a periodic timer with an
interval of 0.1 emulated seconds that is active during recording. The timer
handler processes the wave data recorded with platform or library specific
function and finally notifies the emulator.</P
><P
>The constructor of the base class only initializes the timer ID. OS specific
implementations should initialize other required members here.</P
><P
>The destructor of the base class only calls <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>stopwaverecord()</I
></SPAN
>.
OS specific implementations should close the input device here if necessary.</P
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN854"
>2.9.6.1. int openwaveinput(char *device, sound_record_handler_t rh)</A
></H3
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveinput()</I
></SPAN
> is called when the sound emulation first
receives a sound recording command. It should do the following:</P
><P
></P
><UL
><LI
><P
>Open the given device, and prepare it for wave input</P
></LI
></UL
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>or</I
></SPAN
></P
><P
></P
><UL
><LI
><P
>Store the device name so that the device can be opened in <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>startwaverecord()</I
></SPAN
>.</P
></LI
></UL
><P
>In addition to this the record handler value should be stored and the record timer
should be registered. This is the definition of record handler callback function:
<PRE
CLASS="SCREEN"
>typedef Bit32u (*sound_record_handler_t)(void *arg, Bit32u len);</PRE
></P
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveinput()</I
></SPAN
> will only be called once, whereas
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>startwaverecord()</I
></SPAN
> is called for every new wave input
command to the soundcard emulation. If feasible, it could be useful to open
and/or lock the input device in <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>startwaverecord()</I
></SPAN
> as
opposed to <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openwaveinput()</I
></SPAN
> to ensure that it can be used
by other applications while Bochs doesn't need it.</P
><P
>The parameters are the following: </P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>device</I
></SPAN
> is the wave device selected by the user. It is
strictly system-dependent. The value is that of the <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>wavein=device</I
></SPAN
>
configuration parameter of the <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>sound</I
></SPAN
> bochsrc option.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>rh</I
></SPAN
> is a pointer to the record handler method of the sound
emulation. When sound recording is active, this handler is called periodicly to
notify the sound emulation about newly available data.</P
></LI
></UL
><P
>Note that only one wave input device will be used at any one time.
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>device</I
></SPAN
> may not have the same value throughout one session,
but it will be closed before it is changed.</P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN886"
>2.9.6.2. int startwaverecord(bx_pcm_param_t *param)</A
></H3
><P
>This method receives a pointer to the required PCM parameters (samplerate,
data format) as the argument and it should set up the input device for recording,
calculate the size of the recording packet for 0.1 second and start the record
timer.</P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN889"
>2.9.6.3. int getwavepacket(int length, Bit8u data[])</A
></H3
><P
>This method is called from the record handler method of the sound emulation device
to retrieve the recorded wave data packet.</P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN892"
>2.9.6.4. int stopwaverecord()</A
></H3
><P
>This method is called to stop the wave recording. It deactivates the timer that
calls the method to perform the recording.</P
></DIV
></DIV
><DIV
CLASS="SECTION"
><H2
CLASS="SECTION"
><A
NAME="AEN895"
>2.9.7. The <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>midiout</I
></SPAN
> base class <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>bx_soundlow_midiout_c</I
></SPAN
></A
></H2
><P
><PRE
CLASS="SCREEN"
>class bx_soundlow_midiout_c : public logfunctions {
public:
bx_soundlow_midiout_c();
virtual ~bx_soundlow_midiout_c();
virtual int openmidioutput(const char *mididev);
virtual int midiready();
virtual int sendmidicommand(int delta, int command, int length, Bit8u data[]);
virtual int closemidioutput();
};</PRE
></P
><P
>The base class for MIDI output support is also derived from the
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>logfunctions</I
></SPAN
> class.</P
><P
>OS specific implementations should initialize required members in the constructor.</P
><P
>The destructor of the base class only calls <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>closemidioutput()</I
></SPAN
>.
OS specific implementations should close the input device here if necessary.</P
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN906"
>2.9.7.1. int openmidioutput(char *device)</A
></H3
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openmidioutput()</I
></SPAN
> is called when the first midi output starts.
It is only called if the midi output to the driver is active (midimode 1). It should
prepare the given MIDI hardware for receiving midi commands.</P
></LI
></UL
><P
>Description of the parameters:</P
><P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>mididev</I
></SPAN
> is a system-dependent variable.
The value is that of the <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>midiout=device</I
></SPAN
>
configuration parameter of the <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>sound</I
></SPAN
> bochsrc option.</P
></LI
><LI
><P
>Note that only one midi output device will be used at any one time.
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>device</I
></SPAN
>
may not have the same value throughout one session, but it will be closed
before it is changed.</P
></LI
></UL
></P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN923"
>2.9.7.2. int midiready()</A
></H3
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>midiready()</I
></SPAN
> is called whenever the applications asks if the
midi queue can accept more data.</P
><P
>Return values:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>BX_SOUNDLOW_OK</I
></SPAN
> if the midi output device is ready.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>BX_SOUNDLOW_ERR</I
></SPAN
> if it isn't ready.</P
></LI
></UL
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>Note: </I
></SPAN
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>midiready()</I
></SPAN
> will be called a few times
<SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>before</I
></SPAN
> the device is opened. If this is the case, it should
always report that it is ready, otherwise the application (not Bochs)
will hang.</P
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN939"
>2.9.7.3. int sendmidicommand(int delta, int command, int length, Bit8u data[])</A
></H3
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>sendmidicommand()</I
></SPAN
>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. Note that
if at all possible it should not wait for the completion of the command
and instead indicate that the device is not ready during the execution
of the command. This is to avoid delays in the program while it is
generating midi output.</P
><P
>Description of the parameters:</P
><P
></P
><UL
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>delta</I
></SPAN
> 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.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>command</I
></SPAN
> 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.</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>length</I
></SPAN
> is the number of data bytes that
are contained in the data structure. This does <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>not</I
></SPAN
> include the status
byte which is not replicated in the data array. It can only be greater
than 3 for SysEx messages (commands <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>0xF0</I
></SPAN
> and <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>0xF7</I
></SPAN
>)</P
></LI
><LI
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>data[]</I
></SPAN
> is the array of these data bytes,
in the order they have in the standard MIDI specification.
Note, it might be <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>NULL</I
></SPAN
> if length==0.</P
></LI
></UL
></DIV
><DIV
CLASS="SECTION"
><H3
CLASS="SECTION"
><A
NAME="AEN961"
>2.9.7.4. int closemidioutput()</A
></H3
><P
><SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>closemidioutput()</I
></SPAN
> is called before shutting down Bochs or
when the
emulator gets the <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>stop_output</I
></SPAN
> command through the emulator port.
After this, no more output will be necessary until <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>openmidioutput()</I
></SPAN
>
is called again, but <SPAN
CLASS="emphasis"
><I
CLASS="EMPHASIS"
>midiready()</I
></SPAN
> might still be called. It should
do the following:</P
><P
></P
><UL
><LI
><P
>Wait for all remaining messages to be completed</P
></LI
><LI
><P
>Reset and close the midi output device</P
></LI
></UL
></DIV
></DIV
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="sb16-emulation-basics.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="harddisk-redologs.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Sound Blaster 16 Emulation</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="about-the-code.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Harddisk Images based on redologs</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>