Byte | Mode | Name | Description |
---|---|---|---|
00 | RW | Control | Controll register, write to initiate soundcard operations |
01 | RW | DMA No 0 | Number of buffer to play |
02 | RW | DMA No 1 | Number of buffer to play |
03 | RW | Data Format | Data Format (see below) |
04 | RW | Channels | Number of channels |
05 | RW | Bits per Sample | Number of bits per sample |
06-07 | RW | Samplerate | Number of Samples per channel per second |
08-09 | R | Unused | |
0A | R | Loaded | The number of the buffer that just finished loading |
0B | R | Playing | The number of the buffer that is currently playing |
0C-0F | R | Position | Current position in the buffer just playing |
10-17 | RW | DMA1.address | physical address of first buffer where the next transfer takes place |
18-1F | RW | DMA1.size | size of first buffer where the next transfer takes place |
... | RW | DMA2-15 | more DMA address and size registers |
100-107 | RW | DMA16.address | physical address of 16th and last buffer where the next transfer takes place |
108-10F | RW | DMA16.size | size of 16th and last buffer where the next transfer takes place |
The Control register is used to give commands to the sound card device.The possible command values are:
There are 16 DMA registers that specify a physical buffer address and its size. The address is a full octabyte value; it holds the physical address of the buffer. The size value must be an unsigned tetrabyte value (the hight tetra of the size register is ignored); it's the size of the buffer. Either a single buffer or two alternating buffers can be used together in a playing operation.
The Playing register contains a value from 1 to 16 indicating the DMA buffer that is currently playing. A value of zero indicates that the sond device is currently idle.
If the Plaiyng register is nonzero, the Position register indicates the current position in the buffer; it is a value beween 0 and the buffer size. Data that preceedes this position is already played, and data that follows this position will be played in the future.
The registers DMANo0 and DMANo1 specify up to two buffers to be used in the playing operation. Values between 1 and 16 indicate the buffer to be used. A DMA No register with the value zero is ignored.
The sound device simulates an advanced soundcard that can play different sound formats. As for now, PCM and MP3 Formats are implemented. If the Control Byte speciefies PCM data, the format is specified in the format registers (Byte 03-07). The Data Format Byte can have one of the values specified for the format tag of the RIFF WAVE format. Currently only the Value 0x01 (PCM) is implemented.
|
|
Assume that a short sound, for example a Beep available as MP3 data, needs to be played multiple times. To be specific, let's assume that the mp3 data is located at pysical address 0x100004000 and is 24800 bytes long. We choose DMA registers No 7 to play this sound.
Preparation: We store two octabytes, the address value 0x100004000 at offset 0x70 and the size value 24800 at offset 0x78, into the sound device. Then we issue a Preload command by storing the following octabyte: 0x0307000000000000 at offset 0x00 (alternatively the WYDE value 0x0307 can be stored at offset 0x00, if it is known that the value of DMA No 1 is zero). The 03 is the Preload command, the next byte selects buffer number 7. The sound device might now start to load this data into its local cache.
Playing the sound: To play the Beep sound, it is sufficient to store the following octabyte 0x0107000000000000 at offset 0x00. The first 01 is the PlayOnceMP3 command, the second 07 selects buffer 7 for playing. If the data of buffer no 7 is not yet in the local cache, the sound device will immediately start to load the buffer data. In any case, the sound device will wait until the buffer data has been loaded completely. If some sound is currently playing, playing this sound will be terminated immediately. Then the sound device will start playing the data, interpreting it as MP3 data, from buffer 7 and the Beep can be heard. Upon reaching the end of the data (as given by the size register), the soundcard will stop playing.
While the Beep is playing, reading the Playing register (the byte at offset 0x0B) will return the value 7 and reading the Position register (the octabyte at offset 0x0C) will return any value between 0 and 24800.
Playing the beep a second time just requires storing again the octabyte 0x0107000000000000 at offset 0x00. We can assume that this time the data is already in the local cache. So playing this sound repeatedly requires very little bus activity (just storing one octabyte).
Assume that a short sound, for example a Click, is available as PCM data (for example from a WAV file), and it needs to be played multiple times. To be specific, let's assume that the PCM data comprises two channels of 16 bit samples at sample rate of 22050Hz, is located at pysical address 0x100008000, and is 24800 bytes long,. We choose DMA register No 2 to play this sound.
Preparation: We store two octabytes, the address value 0x100008000 at offset 0x20 and the size value 24800 at offset 0x28, into the sound device. Then, we issue a Preload command by storing the following octabyte: 0x0302000000000000 at offset 0x00 (alternatively the WYDE value 0x0302 can be stored at offset 0x00, if it is known that the value of DMA No1 is zero). The 03 is the Preload command, the next byte selects buffer number 2. The sound device might now start to load this data into its local cache.
Playing the sound: To play the Click sound, it is sufficient to store the following octabyte 0x02 02 00 01 02 10 5622 at offset 0x00. The first 02 is the PlayOncePCM command; the second 02 selects buffer 2 for playing, then next zero byte (DMA No 1) is ignored; the next 01 (Data Format) selects PCM format; then next byte 02 indicates two channel data (stereo); the next byte 10 indicated 16 bit per sample; and the final 0x5622 = 22050 is the sample rate. If the buffer data of buffer number 2 is not yet in the local cache, the sound device will immediately start load the buffer data. In any case, the sound device will wait until the buffer data has been loaded completely. If some sound is currently playing, playing this sound will be terminated immediately. Then the sound device will start playing the data, interpreting it as PCM data, from buffer 2 and the Click can be heard. Upon reaching the end of the data (as given by the size register), the soundcard will stop playing.
While the Click is playing, reading the Playing register (the byte at offset 0x0B) will return the value 7 and reading the Position register (the octabyte at offset 0x0C) will return any value between 0 and 24800.
Playing the beep a second time, requires storing again the octabyte 0x02 02 00 01 02 10 5622 at offset 0x00. We can assume that this time the data is already in the local cache. So playing this sound repeatedly requires very little bus activity (just storing one octabyte).
Normal sound cards usually work with double buffering, assuming that the sound cards access to main memory is fast enough to switch from one buffer to the next without causing an audible interruption in the playing of sound. Depending on the speed of the your host computer (and the virtual bus), this is a demanding real-time task. Here is how we can do it:
Assume that we want to play a large sound file, containing MP3 data, stored on the harddisk. To do so, we allocate two buffers each 16kByte long at the pysical address 0x0000000000300000 and 0x 0000000000320000. We choose DMA registers 1 and 2 to play the file.
Preparation: We initialize the two buffers with the first 2x16KByte of MP3 data from the file. We store address 0x0000000000300000 at offset 0x10 (DMA1.address) and size 0x4000 (16kByte) at offset 0x18 (DMA1.size); and we store address 0x0000000000320000 at offset 0x20 (DMA2.address) and size 0x4000 (16kByte) at offset 0x28 (DMA2.size).
An important issue here is the choice of the buffer size: The sound card simulator uses itself double buffering to supply data to the physical sound card. The buffer size is determined in such a way, that a buffer will contain sound data for about 0.2 seconds. In order for the simulated double buffering to work correctly, the (virtual) buffer size should be large enough to contain data for at least these 0.2 seconds. The sound card simulator fills its first buffer with data, starts plaing and immediately stats filling its second buffer, in order to have the data ready when the first buffer finishes playing. If the firts two virtual buffers do not contain enough data to fill these two real buffers, the sound card simulator will start reading the first virtual buffer a second time without delay after reading the second virtual buffer. It will then be impossible to refresh the data in the first virtual buffer fast enough.
Playing the sound: To start playing the sound file, we store the following octabyte 0x8501020000000000 at offset 0x00. The first 85 is the PlayLoopMP3 command combined with the BufferInterrupt command, the following 0102 selects buffers 1 and 2 for playing, and the last five zero bytes are ignored.
The soundcard will start loading 0.2 seconds worth of data from buffer 1 and then will start playing it. During this time, reading the Playing register (the byte at offset 0x0B) will return the value 1. Buffer 1 sould be large enough (see above) to contain enough data for the first 0.2 seconds.
As soon as the data in buffer 1 is completely loaded to the soundcard, the sound device will raise an interrupt, because the BufferInterrupt bit (0x80) was set in the Control register. It will then continue loading data from buffer 2.
Upon receiving the interrupt, the CPU can check the Loaded register (or use its own counter) to determine that buffer 1 was just completely loaded. It should then load the next 16kByte of data from the file into buffer 1 which will (hopefully) finish before the data in buffer 2 has finished playing.
As soon as the data in buffer 2 has finished loading, the sound device will raise again an interrupt, because the BufferInterrupt bit (0x80) is set in the Control register. It will then continue playing with the new data from buffer 1.
Upon receiving this interrupt, the CPU should load the next 16kByte of data from the file into buffer 2 which will (hopefully) finish before the data in buffer 1 has finished playing.
This process is repeated until the end of file is reached. In this case, the last buffer might contain less than 16kByte of valid data. The CPU should then store the correct size value in the buffers size register. When the next interrupt arrives, there is no more data to be stored in the next buffer. Instead its size value is set to zero. When both buffers involved in the PlayLoop command have a size value of zero, the playing stops automatically. There is no need to issue a Cancel command upon receiving the interrupt caused by finishing the last non-empty buffer.
host | localhost | the host where the bus is located |
port | 9002 | the port where the bus is located |
address | default | address where the resource is located |
interrupt | not set | interrupt sent when character is ready |
x | 0 | the x position of the window |
y | 0 | the y position of the window |
minimized | false | start with a minimized window |
debug | false | to generate debug output |
debugmask | 0xFFF0 | set mask to hide debug output |
verbose | false | make debugging verbose, sets debug mask to zero |
define | not set | to define a name for conditionals |
config | not set | filename for a configuration file |
help | false | to print information |
These can be set in default.vmb