UltraSound Software Development Kit (SDK) Revision 2.10 December 24th, 1993 Documentation by Kurt Kennett and Mike Travers Advanced Gravis 101-3750 North Fraser Way Burnaby, British Columbia V5J 5E9 FAX (604)-431-5155 Forte Technologies 1555 East Henrietta Rd. Rochester, N.Y. 14526 FAX (716)-292-6353 NOTICE The information contained in this manual is believed to be correct. The manual is subject to change without notice and does not represent a commitment on the part of FORTE, Advanced Gravis, or Ingenuity Software. Neither FORTE, Advanced Gravis, nor Ingenuity Software make a warranty of any kind with regard to this material, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. Neither FORTE, Advanced Gravis, nor Ingenuity Software shall be liable for errors contained herein or for incidental or consequential damages in connection with the furnishing, performance or use of this material. This document contains proprietary information which is protected by copyright. This manual is Copyright (C) 1992,1993 by FORTE, Advanced Gravis, and Ingenuity Software. All rights are reserved. No part of this document may be reproduced, transmitted, transcribed, stored in a retrieval system, or translated into any human or computer language, in any form or by any means; electronic, mechanical, magnetic, optical, chemical, manual or otherwise, without the expressed written permission of FORTE, Advanced Gravis, and Ingenuity Software. Any copying, duplication, selling, or otherwise distributing the program or support files described in this manual, other than for the limited purposes of system backup and loading the program into the computer as part of executing the program, is a violation of the software license agreement and the law. Willful violation of the copyright law of the United States can result in statutory damages of up to $50,000 in addition to actual damages, plus criminal penalties of imprisonment for up to one year and/or a $10,000 fine. TABLE OF CONTENTS Section Page Chapter 1 - General Information 3 1.0 Introduction 3 1.1 Features of the UltraSound 3 1.2 Benefits of supporting the UltraSound 4 1.3 The GF1 - 32 Voice Sound Synthesizer 4 1.4 MIDI Interface 7 1.5 Joystick Interface 7 Chapter 2 - Hardware Information 8 2.1 I/O Port Map 8 2.2 MIDI Control Port 9 2.3 MIDI Status Port 9 2.4 MIDI Data Port 9 2.5 Page Register 10 2.6 Select Register 10 2.6.1 Global Registers 10 2.6.1.1 DRAM DMA Control Register 11 2.6.1.2 DMA Start Address 11 2.6.1.3 DRAM I/O Address 12 2.6.1.4 Timer Control 12 2.6.1.5 Timer 1 and Timer 2 Count 12 2.6.1.6 Sampling Frequency 12 2.6.1.7 Sampling Control Register 13 2.6.1.8 Joystick Trim DAC 13 2.6.1.9 Reset Register 14 2.6.2 Voice-specific Registers 14 2.6.2.1 Voice Control Register 15 2.6.2.2 Frequency Control Register 16 2.6.2.3 Starting location HIGH 16 2.6.2.4 Starting location LOW 16 2.6.2.5 End Address HIGH 16 2.6.2.6 End Address LOW 16 2.6.2.7 Volume Ramp Rate 17 2.6.2.8 Volume Ramp Start 17 2.6.2.9 Volume Ramp End 17 2.6.2.10 Current Volume 18 2.6.2.11 Current Location HIGH 18 2.6.2.12 Current Location LOW 18 2.6.2.13 Pan Position 18 2.6.2.14 Volume Ramp Control Register 19 2.6.2.15 Active Voices 19 2.6.2.16 IRQ Source Register 20 2.7 Global Data Low 20 2.8 Global Data High 20 2.9 IRQ Status 21 2.10 Timer Control Register 21 2.11 Timer Data Register 21 2.12 DRAM I/O 21 Section Page 2.13 Mix Control Register 22 2.14 IRQ Control Register 22 2.15 Register Control 24 2.16 Volume ramping description 24 Chapter 3 - Programming the UltraSound 26 3.0 Introduction 26 3.1 Sound 26 3.2 The Basics of the UltraSound 27 3.3 Using GUS Memory 27 3.4 What are Samples? 28 3.5 Using Voices 29 3.6 Volumes 30 3.7 Using Looping 30 3.8 Clicks and click removal 31 3.9 Interrupt Handling Functions 32 3.10 Rollover feature 32 3.11 Stereo playback 33 3.12 C-specific information 35 3.13 PASCAL-specific information 36 3.13.1 Available constants and variables 37 3.13.2 Examples 39 3.13.3 Management of GUS RAM 40 3.14 Coming Attractions 41 3.15 Technical Support 42 Chapter 4 - Reference Guide 43 UltraAllocVoice 81 UltraAuxHandler 54 UltraCalcRate 43 UltraClearVoices 81 UltraClose 43 UltraDisableLineIn 46 UltraDisableMicIn 46 UltraDisableMIDIXmit 58 UltraDisableOutput 47 UltraDownload 44 UltraDRAMDMABusy 45 UltraDRAMTcHandler 50 UltraEnableLineIn 47 UltraEnableMicIn 47 UltraEnableMIDIXmit 59 UltraEnableOutput 48 UltraFreeVoice 82 UltraGetLineIn 48 UltraGetMicIn 49 UltraGetOutput 48 UltraGoRecord 45 UltraGoVoice 46 UltraMaxAlloc 55 UltraMaxAvail 55 Section Page UltraMemAlloc 56 UltraMemAvail 56 UltraMemFree 57 UltraMemInit 57 UltraMIDIDisableRecv 58 UltraMIDIEnableRecv 58 UltraMIDIRecv 59 UltraMIDIRecvHandler 51 UltraMIDIReset 60 UltraMIDIStatus 60 UltraMIDIXmit 60 UltraMIDIXmitHandler 51 UltraOpen 61 UltraPeekData 62 UltraPing 62 UltraPokeData 63 UltraPrimeRecord 64 UltraPrimeVoice 65 UltraProbe 66 UltraRampLinearVolume 84 UltraRampVolume 67 UltraReadLinearVolume 84 UltraReadRecordPosition 67 UltraReadVoice 68 UltraReadVolume 68 UltraRecordData 69 UltraRecordDMABusy 70 UltraRecordHandler 54 UltraReset 70 UltraSetBalance 71 UltraSetFrequency 71 UltraSetLinearVolume 83 UltraSetLoopMode 71 UltraSetRecordFrequency 72 UltraSetVoice 72 UltraSetVoiceEnd 72 UltraSetVolume 73 UltraSizeDRAM 73 UltraStartTimer 74 UltraStartVoice 75 UltraStopTimer 75 UltraStopVoice 76 UltraStopVolume 76 UltraTimer1Handler 52 UltraTimer2Handler 52 UltraTimerStopped 76 UltraTrimJoystick 77 UltraUpload 77 UltraVectorLinearVolume 85 UltraVectorVolume 78 UltraVersion 78 UltraVersionStr 79 Section Page UltraVoiceOff 82 UltraVoiceOn 83 UltraVoiceStopped 79 UltraVolumeHandler 53 UltraVolumeStopped 79 UltraWaitDRAMDMA 80 UltraWaitRecordDMA 80 UltraWaveHandler 53 Chapter 5 - Focal Point 3D Sound 86 5.0 Introduction 86 5.1 Creating 3D file 87 5.2 3D Sound Routines 87 UltraAbsPosition 87 UltraAngPosition3D 88 UltraAngFltPosition3D 89 UltraCloseDup3D 89 UltrDup3D 90 UltraLoad3DEffect 91 UltraSetFreq3D 92 UltraRelease3DInterleave 92 UltraSetup3DInterleave 93 UltraStart3D 93 UltraStop3D 94 UltraUnLoad3DEffect 94 Appendix A - Error Codes 95 Appendix B - Volume Control 96 Appendix C - Voice Control 97 Appendix D - DMA Control 98 Appendix E - Recording Control 99 Appendix F - Patch Files 100 Field descriptions 104 Appendix G - 3D File Header 109 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Chapter 1 - General Information 1.0 Introduction This is version 2.10 of the Software Development Kit (SDK) for the Advanced Gravis UltraSound card, and is the combined work of Advanced Gravis, FORTE Technologies, and Ingenuity Software. This kit is intended for use by programmers of IBM-compatible PCs. After becoming familiar with this material, you should be able to use C or Pascal to program the UltraSound under MS-DOS. This is the first version which includes a full Borland Pascal (version 7) and Turbo-Pascal (versions 6 and 7) interface. This was written by Kurt Kennett of Ingenuity Software for Advanced Gravis, and is the direct translation of the routines in the C interface. For specific information about the Pascal interface, see section 3.13. For specific information about the revised and updated C interface, including compilers and memory models supported, see section 3.12. Some of the discussions of the hardware interface to the card (Chapter 2) are quite technical, and go beyond the level of technical sophistication needed to write software. Therefore, a seperate section (Chapter 3) has been included which is a general overview of how to write software for the card. What follows in this chapter is a general description of the UltraSound card and its features, the MIDI functionality, the Joystick interface, and the GF1 32-voice Sound Synthesizer. Throughout this documentation, the words 'lowlevel toolkit', 'lowlevel routines' and 'lowlevel code' are used. This refers to any and all of the routines described and included in the C and/or Pascal interfaces. 1.1 Features of the UltraSound - Jumper selectable base port address. - Software selectable IRQ vectors and DMA channels. - XT and AT compatibility. - 8 or 16 bit playback: Stereo and Monophonic. - 8 bit recording: Stereo or Monophonic. - Playback and recording rates up to 44.1 kHz. - 32 voice wavetable synthesis: all voices mixed on board. - Simultaneous playback and recording is possible. - Each voice has its own volume settings, volume enveloping, playback rate and balance. - Both line level and amplified outputs. - 6850 compatible MIDI UART on-board. - Gravis Eliminator joystick interface with jumper enable/disable. - 256Kb DRAM on board for waveforms. Expandable to 1Mb. - Stereo microphone input with automatic level control. - Line level input. - CD ROM Drive audio input. - Stereo mini-jacks for line & amplified outputs and Mic and Line inputs. ___________________________________________________________________________ 3 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 1.2 Benefits of supporting the UltraSound The UltraSound has 32 separate voices, each of which can be used to play back any 8- or 16-bit digital data that is loaded into its DRAM. This data can consist of individual sound effects, a whole sound track, or even single musical notes of a digitized instrument. The UltraSound is completely controlled by the PC, thus giving maximum flexibility to applications which use it. The main chip which runs the card (the GF1) is directly addressable from the PC's CPU via basic I/O instructions. Here are some of the features that the UltraSound offers: 1) Wavetable synthesis has a much higher sound quality that FM-based cards can ever achieve. This method of audio synthesis is the same method used in expensive professional keyboards. 2) 32 digital voices can be independently controlled. This allows you to have up to 32 sound effects or instruments going at once. 3) The card is RAM-based. Since the instrument patches are kept on disk and only loaded when they are needed, they can be changed or improved at will. 4) Focal Point 3D sound generation. You can position your applications sounds anywhere in 3D space around the listener. 5) All mixing is done on the card. There is no burden on the CPU to playback multiple channels. 6) Multiple cards can be installed in the same PC. With some relatively minor adjustments to the SDK software, multiple cards can be controlled in the same PC. 7) Hardware-assisted voice enveloping. A multi-point attack-decay-sustain- release envelope can be implemented that uses virtually no CPU overhead. Tremelo can also be done in hardware. 8) Sound effects can be pre-loaded into DRAM, using little or no PC memory to keep track of them. This leaves more space for your application and it's data. It also allows you to start or stop any sound whenever you wish. 1.3 The GF1 - 32 Voice Sound Synthesizer The UltraSound uses Wave-Table Synthesis to produce sound output. This means that either sampled data from actual instruments or other synthesized digital audio is stored in DRAM on the UltraSound Board. The GF1 chip which controls the board is set up to play back relatively short digital audio samples and produce continuous sound closely reproducing the original instrument. The 32 voices are independently controlled and can be producing different sounds concurently. Output from all voices is mixed into the left or right channels. Circuitry in the GF1 can be programmed to perform the following audio processing functions independently for each voice: - Frequency Shifting to produce different notes of the same instrument. ___________________________________________________________________________ 4 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ - Amplitude Modulation to produce note enveloping (attack, decay, sustain, release), overall volume control or special effects such as LFO (low frequency amplitude modulation). - Panning the voice from left to right channel outputs. Multi-track digital audio recordings can be played through the GF1 by using one voice per digital audio track. The GF1 is compatible with 8 and 16 bit data, stereo or mono, signed or unsigned data, and 8 or 16 bit DMA channels. The GF1 is basically a pipeline processor. It constantly loops from voice #0 to the end of the active voices (how to define the number of active voices is shown later). Every 1.6 microseconds, the GF1 performs a series of operations on a particular voice. The more active voices there are, the longer it takes between each time a particular voice is serviced. This puts a limit on the rate at which playback can occur. 14 active voices will allow a maximum of 44.1 kHz playback. 28 voices will allow 22 kHz. Faster rates can be achieved by making the frequency constant greater than 1. This will cause the GF1 to skip some data bytes to play a sample back at the requested frequency. This is not generally a problem, but could cause some distortion or aliasing. The formula for calculating the playback rate is: Frequency Active Voices 44100 14 41160 15 38587 16 36317 17 34300 18 32494 19 30870 20 29400 21 28063 22 26843 23 25725 24 24696 25 23746 26 22866 27 22050 28 21289 29 20580 30 19916 31 19293 32 This table is calculated by knowing that 14 active voices will give exactly 44.1 kHz playback. Therefore, the voice servicing rate 'X' can be calculated from: 1,000,000 / (X * 14) = 44100 Hz X = 1.619695497 microseconds ___________________________________________________________________________ 5 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Once X is known, the frequency 'divisor' is calculated by: divisor = 1,000,000 / (1.619695497 * # of active voices) The lowlevel code pre-calculates this table so that floating point arithmetic doesn't need to be done. A frequency 'counter' is used to calculate how often a voice is updated by the GF1. To calculate an FC (frequency counter) for any given frequency with a particular # of active voices, run it through this formula: C: fc = (unsigned int)(((speed_khz<<9L)+(divisor>>1L)) / divisor); fc = fc << 1; PASCAL: fc := Word(((Speed_Khz SHL 9)+(Divisor SHR 1)) DIV Divisor); fc := fc SHL 1; The left shift is needed since the FC is in bits 15-1. For more information about the frequency counter, see chapter 2, the hardware section. This value is then put in the frequency control register for that particular voice. If the mantissa portion of the FC is 1, then each time around the loop, the GF1 uses each data point to play. If there is a fractional portion, the GF1 interpolates the actual data to play from the two data points that it is between. This makes the sound much 'smoother', since the GF1 will create points in between the actual data points. For example, assume an 8 bit recording at 22 kHz and 14 active voices . The frequency control register is set up to 1/2 (exponent = 256). This means that every time around the loop, that particular voice's accumulator is adjusted by 1/2. So the first time the accumulator is 0 and data point 0 is used. The second time around the loop, the accumulator is 0.5. Since there obviously is no DRAM location 0.5, the GF1 interpolates what the data would be by looking at location 0 and location 1 and taking the appropriate ratio from each. In this case, it picks a point half-way between the two. If the recording rate were 11kHz, it would take 25% from location 0 and 75% from location 1 the first time through the loop. The next time it would take 50% from each. The next time it would take 25% from location 0 and 75% from location 1. The fourth time through it uses 100% of location 1. The interpolation is done to a resolution of 16 bits, even for 8 bit playback. This has the effect of making an 8 bit recording sound better when played back on the GF1 than on a standard 8 bit card. Remember that the GF1 works on a voice every 1.6 microseconds. This means that the fewer voices, the faster each voice gets updated. The frequency control register setting for the voice MUST take this into account. The FC must get smaller if the number of active voices gets smaller. This will increase the number of points created between the actual data points so the perceived playback speed remains the same. ___________________________________________________________________________ 6 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 1.4 MIDI Interface The MIDI 101 interface consists of standard UART functionality - Motorola MC68C50. An interrupt to the PC can be generated for each byte of MIDI data received or transmitted. This hardware is independent of any of the other hardware. The main MIDI circuitry is included in the GF1 processor, but external to the chip is an optical isolator that is used on the serial input data and an open collector driver that is used for the serial output. In addition, external logic is included on board to loop back transmit data to the receive data under software control. The serial interface has a fixed configuration with no programmable options, as in the MC6850. A control register is used to enable and disable the interrupt generation logic. A status register is used to determine if the transmit or receive register is interrupting. A read or write to the data register clears the interrupt status. The specifications for the interface are: 31.25 kHz +- 1% asynchronous 1 start bit 8 data bits 1 stop bit The MIDI signals are available on the 15 pin D connector used for the joystick. An external cable assembly containing the optical isolator and driver is required to use the MIDI interface. 1.5 Joystick Interface The joystick interface is an Eliminator Joystick interface designed by Advanced Gravis. It basically consists of a single eight bit register. When written to, four flip-flops are reset and comparator inputs (LM339) begin to charge up based on the position of the joystick. The comparator threshold is setup in the GF1. Crossing the threshold of the comparators cause the flip-flops to be preset and the capacitor to be discharged. Reads of the register return the state of four digital inputs (internally pulled up) and the state of the flip-flops. The rate of discharge of the capacitors has a minimum time constant of 1 microsecond. UltraSound boards with a revision version (printed on the card) of 3.3 and lower have a jumper which is used to enable or disable the joystick. Boards with a revision version of 3.4 and above have a software enable/disable for the joystick instead of a jumper. There is a joystick SDK available sperately from Gravis. ___________________________________________________________________________ 7 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Chapter 2 - Hardware Information 2.1 I/O Port Map The following describes I/O address map used on the board. The 'X' is defined by the jumper settings on the UltraSound and should match that specified in the ULTRASND environment variable. INTERFACE I/O,MEM, R,W ADDRESS INT,DMA HEX ------------------------------------------------------------ UltraSound Base Port: --- -- 2X0 MIDI Interface: Control I/O W 3X0 Status I/O R 3X0 Transmit Data I/O W 3X1 Receive Data I/O R 3X1 Joystick Interface: Trigger Timer I/O W 201 Read Data I/O R 201 GF1 Synthesizer: GF1 Page Register I/O R/W 3X2 GF1/Global Register Select I/O R/W 3X3 GF1/Global Data Low Byte I/O R,W 3X4 GF1/Global Data High Byte I/O R/W 3X5 IRQ Status Register 1=ACTIVE I/O R 2X6 Timer Control Reg I/O R/W 2X8 Timer Data I/O W 2X9 DRAM I/O R,W 3X7 DRAM DMA R,W 1,3,5,6,7 Record Digital Audio DMA R 1,3,5,6,7 BOARD ONLY Mix Control register I/O W 2X0 IRQ control register I/O W 2XB (2X0- bit 6 = 1) DMA control register I/O W 2XB (2X0- bit 6 = 0) Register Controls I/O R/W 2XF (Rev 3.4+) Mixer Control I/O W 3X6 (Rev 3.7+) At power-up the board has operational Joystick and MIDI interfaces. This allows their direct use with existing software. The GF1 ASIC powers up with all voices disabled, not requiring a software initialization. This helps eliminate noise at powerup and allows the Joystick and MIDI interfaces to be used by existing applications. The IRQ control register MUST be set up before the MIDI interface can generate an IRQ. This is done in ULTRINIT.EXE and when an application sets up the latches to the ULTRASND parameters. ___________________________________________________________________________ 8 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.2 MIDI Control Port - 3X0 Here are the bit definitions for the MIDI control byte. It is located at address 3X0 hex and is write only. ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- 1 \ Master reset (when set) | | | | | | +-------- 1 / | | | | | +------------ Reserved | | | | +---------------- Reserved | | | +-------------------- Reserved | | +------------------------ 1 \ xmit IRQ enabled | +---------------------------- 0 / +-------------------------------- 1 = Receive IRQ enabled Bit 0 & 1 will cause a master reset when toggled high and then low. They must be left low when using port. This will normally cause a transmit buffer empty IRQ. 2.3 MIDI Status Port - 3X0 Here are the bit definitions for the MIDI status byte. It is located at address 3X0 hex and is read-only. ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Receive reg. full | | | | | | +-------- Transmit reg. empty | | | | | +------------ Reserved | | | | +---------------- Reserved | | | +-------------------- Framing Error | | +------------------------ Overrun error | +---------------------------- Reserved +-------------------------------- Interrupt pending The MIDI control interface behaves identically to a 6850 UART. 2.4 MIDI Data Port - 3X1 The transmit and receive registers are at 3X1 hex and are 8 bits wide. ___________________________________________________________________________ 9 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.5 Page Register - 3X2 This could also be called the voice select register. This register is used to specify which voice's registers you want to read/write. This value can range from 0 to the number of active voices specified (13-31). Once this has been specified, you may select the specific register within that voice. Be careful that IRQs are off during the time that the page and select registers are being modified. This will prevent the foreground from selecting a voice and having the background change it in the background. 2.6 Select Register - 3X3 2.6.1 Global Registers These are the global registers. They are not voice-specific. Address Mode Width Description 41 R/W 8 DRAM DMA Control 42 W 16 DMA Start Address 43 W 16 DRAM I/O Address (LOW) 44 W 8 DRAM I/O Address (HIGH) 45 R/W 8 Timer Control 46 W 8 Timer 1 Count 47 W 8 Timer 2 Count 48 W 8 Sampling Frequency 49 R/W 8 Sampling Control 4B W 8 Joystick trim DAC 4C R/W 8 RESET ___________________________________________________________________________ 10 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.6.1.1 DRAM DMA Control Register - (41) ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Enable DMA (1=go) | | | | | | +-------- DMA direction (1=read) | | | | | +------------ DMA channel width | | | | +---------------- \ | | | +-------------------- / DMA Rate divider | | +------------------------ DMA IRQ Enable | +---------------------------- (R) DMA IRQ Pending | (W) DATA SIZE (0=8bit,1=16bit) +-------------------------------- Invert MSB (write only) Bit 0 - Enable the DMA channel. The GF1 will begin sending DMA ACK protocol. If PC DMA controller is programmed, data will begin being transferred. If not, data will move as soon as it is programmed. Bit 1 - DMA transfer direction. Read is taking data OUT of the UltraSound, Write sends data to it. Bit 2 - 0 = if DMA channel is an 8 bit channel (0-3). 1 = If it is a 16 bit channel (4-7) Note: This is INDEPENDENT of the data size. Bit 3,4 -DMA Rate divisor. The Maximum rate is approx 650 khz. 00 = divide by 1 01 = divide by 2 10 = divide by 3 11 = divide by 4 Bit 5 - DMA terminal count interrupt enable Bit 6 - Read - DMA terminal count IRQ pending Write - Data size 0 = 8 Bit data 1 = 16 bit data Note: Data size is independent of channel size Bit 7 - 1 = Invert High bit to flip data to twos complement form. Note: This flips bit 7 for 8 bit data and bit 15 for 16 bit data. 2.6.1.2 DMA Start Address - (42) Bits 15-0 are Address lines 19-4. This register defines where the DMA will transfer data to or from. Since only the upper 16 address bits are used and the lower 4 bits are set to 0, a DMA transfer MUST begin on an 16 byte boundary for an 8 bit DMA channel (0-3). If a 16 bit DMA channel is being used, the transfer MUST being on a 32 byte boundary. An additional address translation is necessary if a 16 bit DMA channel is used. For simple example code on how to do this translation, see the C function convert_to_16(). ___________________________________________________________________________ 11 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.6.1.3 DRAM I/O Address (43,44) These 2 registers allow you to specify an address to peek and poke directly into UltraSound DRAM. Register 43 is the lower 16 address lines. Register 44 is the upper 4 address lines. (bits 0-3). Read or write to register 3X7 to get at the address location. 2.6.1.4 Timer Control - (45) ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Reserved (Set to 0) | | | | | | +-------- Reserved (Set to 0) | | | | | +------------ Enable Timer 1 IRQ | | | | +---------------- Enable Timer 2 IRQ | | | +-------------------- Reserved (Set to 0) | | +------------------------ Reserved (Set to 0) | +---------------------------- Reserved (Set to 0) +-------------------------------- Reserved (Set to 0) 2.6.1.5 Timer 1 and Timer 2 Count - (46,47) These counts are loaded by the application and then they will count up to $FF and generate and IRQ. Timer 1 has a granularity of 80 microseconds (0.00008 sec) and Timer 2 has a granularity of 320 microseconds (0.00032 sec). 2.6.1.6 Sampling Frequency - (48) The formula for calculating this value is: rate = 9878400/(16*(FREQ+2)) ___________________________________________________________________________ 12 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.6.1.7 Sampling Control Register - (49) ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Start sampling | | | | | | +-------- Mode (0=mono, 1=stereo) | | | | | +------------ DMA width (0=8bit,1=16bit) | | | | +---------------- Reserved (Set to 0) | | | +-------------------- Reserved (Set to 0) | | +------------------------ DMA IRQ enable | +---------------------------- (Read) DMA IRQ pending +-------------------------------- Invert MSB Bit 0 -If PC DMA controller is programmed, it will begin sampling as soon as this is enabled. Bit 1 -0 = mono 1 = stereo In stereo mode, the order of the data bytes is left is first, and right is second. If a 16 bit data channel is used, the left is in the lower byte. Bit 2 -DMA Channel width (0 = 8 bit, 1 = 16 bit) Bit 5 -Enable DMA terminal count IRQ Bit 6 -DMA terminal count IRQ pending Bit 7 -Flip bit 7 to get non-twos compliment data 2.6.1.8 Joystick Trim DAC - (4B) This register is initialized to 4.3 volts (value = 29). It only needs to be modified to account for faster/slower machines. A utility is provided (ULTRAJOY.EXE) that sets this up. There should be no reason for your application to modify this register. ___________________________________________________________________________ 13 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.6.1.9 Reset Register - (4C) ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Master Reset | | | | | | +-------- DAC Enable | | | | | +------------ GF1 Master IRQ Enable | | | | +---------------- Reserved (Set to 0) | | | +-------------------- Reserved (Set to 0) | | +------------------------ Reserved (Set to 0) | +---------------------------- Reserved (Set to 0) +-------------------------------- Reserved (Set to 0) Bit 0 -GF1 Master Reset. 0 = reset, 1 = run. As long as this is a 0, it will be held in a reset state. Bit 1 -Enable DAC output. DAC's will not run unless this bit is set. Bit 2 -Master IRQ enable. This bit MUST be set to get ANY of the GF1- generated IRQs (wavetable, volume, etc). This register will normally contain the value $07 while your application is running. 2.6.2 Voice-specific Registers The following are the voice-specific registers. Each voice has its own bank of read and write registers that alter its behavior. The write registers range from 0 to F and the corresponding read registers range from 80 to 8F. To convert from the write to the read, just add 80 hex. Write Read Width Description 0 80 8 Voice Control 1 81 16 Frequency Control 2 82 16 Starting Address (HIGH) 3 83 16 Starting Address (LOW) 4 84 16 End Address (HIGH) 5 85 16 End Address (LOW) 6 86 8 Volume Ramp Rate 7 87 8 Volume Ramp Start 8 88 8 Volume Ramp End 9 89 16 Current Volume A 8A 16 Current Address (HIGH) B 8B 16 Current Address (LOW) C 8C 8 Pan Position D 8D 8 Volume Control E 8E 8 Active Voices (Voice independent) - 8F 8 IRQ Status (Voice independent) There are several 'self-modifying' bits defined for these registers. This means that the GF1 may change them at anytime on its own. Due to the fact that the software must accommodate this phenomena, it is possible that the ___________________________________________________________________________ 14 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ GF1 may change something immediately after your application has set/reset one of the bits. This is due to the GF1's pipeline processor type of architecture: it does a read-modify-write cycle, and if your application modifies one of these bits AFTER it has done the read portion and BEFORE it does the write portion, it's possible for the chip to perform incorrectly. To overcome this, you need to do a double write (with a delay in between) when those particular bits are involved. This delay must be at least 3 times the length of time necessary to process a voice. (3*1.6 microsecs). In the lowlevel code, this is done with a function called GF1_Delay. The self-modifying bits are designated with an (*) after the particular bit definition. Changing the start and end points of a voice while its playing can have some strange side effects. For example, if you change end poistion to a lower location than it is currently playing, you will get and IRQ (if they are enabled). Also, since the high and low bytes are set individually and asynchronously to when the GF1 is working on a voice, it is possible to get an unexpected IRQ if the current position and the new end position cross. 2.6.2.1 Voice Control Register - (0,80) ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Voice Stopped | | | | | | +-------- Stop Voice | | | | | +------------ 16 bit data | | | | +---------------- Loop enable | | | +-------------------- Bi-directional loop enable | | +------------------------ Wave table IRQ | +---------------------------- Direction of movement +-------------------------------- IRQ pending * Bit 0- 1 = Voice is stopped. This gets set by hitting the end address (not looping) or by setting bit 1 in this reg. Bit 1- 1 = Stop Voice. Manually force voice to stop. Bit 2- 1 = 16 bit wave data, 0 = 8 bit data Bit 3- 1 = Loop to begin address when it hits the end address. Bit 4- 1 = Bi-directional looping enabled Bit 5- 1 = Enable wavetable IRQ. Generate an IRQ when the voice hits the end address. Will generate IRQ even if looping is enabled. * Bit 6- 1 = Decreasing addresses, 0 = increasing addresses. It is self- modifying because it might shift directions when it hits one of the loop boundaries and looping is enabled. * Bit 7- 1 = Wavetable IRQ pending. If IRQ's are enabled and looping is NOT enabled, an IRQ will be constantly generated until the voice is stopped. This means that you may get more than 1 IRQ if it isn't handled properly. ___________________________________________________________________________ 15 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.6.2.2 Frequency Control Register - (1,81) Bits 15-10 - Integer Portion Bits 9-1 - Fractional Portion Bit 0 - Not used. This register determines the amount added to (or subtracted from) the current position of the voice to determine where the next position will be. This is how the interpolated data points are determined. If the FC register is less than 0, the GF1 will interpolate the data point in between the two actual data points. Note that the FC can be greater than 1. This allows for skipping over data bytes. The actual frequency that it will play back is directly related to the number of active voice specified (register 8E). 2.6.2.3 Starting location HIGH - (2,82) Bits 12-0 are the HIGH 13 bits of the address of the starting location of the waveform (Addr lines 19-7). Bits 15-13 are not used. 2.6.2.4 Starting location LOW - (3,83) Bits 15-9 are the low 7 bits of the address of the starting location of the waveform. (Addr lines 6-0). Bits 8-5 are the fractional part of the starting address. Bits 4-0 are not used. 2.6.2.5 End Address HIGH - (4,84) Bits 12-0 are the high 13 bits of the address of the ending location of the waveform. (Addr lines 19-7) Bits 15-13 are not used. 2.6.2.6 End Address LOW - (5,85) Bits 15-9 are the low 7 bits of the address of the ending location of the waveform. (Addr lines 6-0). Bits 8-5 are the fractional part of the ending address. Bits 4-0 are not used. ___________________________________________________________________________ 16 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.6.2.7 Volume Ramp Rate - (6,86) Bits 5-0 is the amount added to (or subtracted from) the current volume to get the next volume. The range is from 1 to 63. The larger the number, the greater the volume step. Bits 7-6 defines the rate at which the increment is applied. Please see section 2.16 for more information on hardware volume ramping. 2.6.2.8 Volume Ramp Start - (7,87) Bits 7-4 Exponent Bits 3-0 Mantissa This register specifies the starting position of a volume ramp. See the special section on volume ramping for a more complete explanation of how this register works. Please see section 2.16 for more information on hardware volume ramping. 2.6.2.9 Volume Ramp End - (8,88) Bits 7-4 Exponent Bits 3-0 Mantissa This register specifies the ending position of a volume ramp. See the special section on volume ramping for a more complete explanation of how this register works. Note: The starting volume must always be less than the ending volume. If you want the volume to ramp down, turn on the decreasing volume bit in the Volume Control Register. Please see section 2.16 for more information on hardware volume ramping. ___________________________________________________________________________ 17 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.6.2.10 Current Volume - (9,89) * Bits 15-12 Exponent * Bits 11-4 Mantissa Bits 3-0 Reserved (Set to 0) Note: This register has 4 extra bits of precision that is necessary for finer granularity of volume placement. The extra bits are used during a volume ramp. Note: This is a self-modifying value. The GF1 will update this register as it ramps. Note: You should always set this register equal to the value of the beginning of the volume ramp (start OR end). Please see section 2.16 for more information on hardware volume ramping. 2.6.2.11 Current Location HIGH - (A,8A) Bits 15-13 Reserved (Set to 0) Bits 12-0 High 13 bits of address (address lines 19-7) 2.6.2.12 Current Location LOW - (B,8B) Bits 15-9 Low 7 bits of address. (address lines 6-0) Bits 8-0 9 bit fractional position. 2.6.2.13 Pan Position - (C,8C) Bits 8-4 Reserved (Set to 0) Bits 3-0 Pan position. (0=full left, 15=full right) ___________________________________________________________________________ 18 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.6.2.14 Volume Ramp Control Register - (D,8D) ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Ramp Stopped | | | | | | +-------- Stop Ramp | | | | | +------------ Rollover condition | | | | +---------------- Loop Enable | | | +-------------------- Bi-directional loop enable | | +------------------------ Volume ramp IRQ enable | +---------------------------- Direction +-------------------------------- IRQ pending * Bit 0- Show the ramp has stopped Bit 1- Manually stop the ramp. Bit 2- Roll over condition. This bit pertains more towards the location of the voice rather than its volume. Its purpose is to generate an IRQ and NOT stop (or loop). It will generate an IRQ and the voice's address will continue to move thru DRAM in the same direction. This can be a very powerful feature. It allows the application to get an interrupt without having the sound stop. This can be easily used to implement a ping-pong buffer algorithm so an application can keep feeding it data and there will be no pops. Even if looping is enabled, it will not loop. Bit 3- Enable looping. Loop from end to start (or start to end). Bit 4- Enable bi-directional looping. When it hits end (or start) it will change directions and proceed toward the other limit. Bit 5- Enable getting an IRQ when ramp hits end. * Bit 6- Ramp direction. 0=increasing, 1=decreasing. * Bit 7- (READ) Volume ramp IRQ pending. Please see section 2.16 for more information on hardware volume ramping. 2.6.2.15 Active Voices - (E,8E) Bits 7-6 Must be set to a 1 Bits 5-0 # of voices to enable - 1. The range is from 14 - 32. Any value less than 14 will be forced to 14. ___________________________________________________________________________ 19 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.6.2.16 IRQ Source Register - (F,8F) ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +----\ | | | | | | +-------- \ | | | | | +------------ - Interrupting voice # | | | | +---------------- / | | | +--------------------/ | | +------------------------ 1 | +---------------------------- Volume Ramp IRQ pending +-------------------------------- WaveTable IRQ pending Bit 4-1-Voice # (0-31) of interrupting voice Bit 5- ALWAYS a 1 Bit 6- 0 = Volume Ramp IRQ occurred Bit 7- 0 = Wavetable IRQ occurred Note: This is a global read only register. There is only 1 for ALL voices. You MUST service any indicated IRQ's since a read of this port will clear the associated IRQ bits in the particular voice's control and/or volume control registers. Note: It is possible that multiple voices could interrupt at virtually the same time. In this case, this register will behave like a fifo. When in your IRQ handler, keep reading (and servicing) this register until you do a read with both IRQ bits set to a 1. This means there are no voice IRQs left to deal with. Note: Since it is possible to get ANOTHER IRQ from the same voice for the SAME reason, you must ignore any subsequent IRQ from that voice while in the IRQ handler. For example, when a voice hits its end position and generates an IRQ back to your application, it will continue to generate IRQ's until either the voice is stopped, the IRQ enable is turned off, or the end location is moved. 2.7 Global Data Low - 3X4 This register can be used to do either a 16 bit transfer for one of the 16 bit wide GF1 registers (Start addr high etc) when using a 16 bit I/O instruction or the low part of a 16 bit wide register when using an 8 bit I/O instruction. 2.8 Global Data High - 3X5 This register is used to do either an 8 bit transfer for one of the GF1 8 bit registers or to do the high part of a 16 bit wide register. ___________________________________________________________________________ 20 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.9 IRQ Status - 2X6 CAUTION: Note that this is at 2X6 *** NOT 3X6 ***. ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- MIDI Transmit IRQ | | | | | | +-------- MIDI Receive IRQ | | | | | +------------ Timer 1 IRQ | | | | +---------------- Timer 2 IRQ | | | +-------------------- Reserved (Set to 0) | | +------------------------ WaveTable IRQ (any voice) | +---------------------------- Volume Ramp IRQ (any voice) +-------------------------------- DMA TC IRQ (DRAM or Sample) 2.10 Timer Control Register - 2X8 This register maps to the same location as the ADLIB board's control register. Writing a 4 here selects the timer stuff. Bit 6 will be set if timer #1 has expired. Bit 5 will be set it timer #2 has expired. 2.11 Timer Data Register - 2X9 ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Timer 1 Start | | | | | | +-------- Timer 2 Start | | | | | +------------ Reserved (Set to 0) | | | | +---------------- Reserved (Set to 0) | | | +-------------------- Reserved (Set to 0) | | +------------------------ Mask Timer 2 | +---------------------------- Mask Timer 1 +-------------------------------- Reset Timer IRQ Bit 0 - Start up timer #1 Bit 1 - Start up timer #2 Bit 5 - Mask off timer 2 Bit 6 - Mask off timer 1 Bit 7 - Clear Timer IRQ 2.12 DRAM I/O - 3X7 This register is used to read or write data at the location pointed at by registers 43 and 44. This is used to peek and poke directly to DRAM. ___________________________________________________________________________ 21 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.13 Mix Control Register - 2X0 ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- 0=Enable Line IN | | | | | | +-------- 0=Enable Line OUT | | | | | +------------ 1=Enable MIC IN | | | | +---------------- Enable latches | | | +-------------------- Combine chan1 IRQ with Chan2 | | +------------------------ Enable MIDI loopback TxD to RxD | +---------------------------- Control Reg Select +-------------------------------- Reserved (Set to 0) Bit 0 -Enable UltraSound line Input Bit 1 -Enable UltraSound Line output Bit 2 -Enable Stereo Mic Input Bit 3 -Enable latches.This provides power to the DMA/IRQ latches. Once these are enabled, NEVER disable them. Disabling them will cause random IRQ's in the PC since the DMA and IRQ lines are not being driven any more. Bit 4 -Combine Channel 1 (GF1) IRQ's with Channel 2 (MIDI) Bit 5 -Enable MIDI loopback. Any data sent out Transmit port will be looped back into the input port. Bit 6 -Control Register Select. When this is set to a 1, the next IO write to 2XB will be to the IRQ control latches. When this is set to a 0, the next IO write to 2XB will be to the DMA channel latches. The write to 2XB for either of these MUST occur as the NEXT IOW or else the write to 2XB will be locked out and not occur. This is to prevent an application that is probing for cards to accidentaly corrupt the latches. 2.14 IRQ Control Register - 2XB IRQ control register I/O W 2XB (2X0- bit 6 = 1) Bits 2-0 Channel 1 (GF1) IRQ Selector 0=No Interrupt 1=IRQ2 2=IRQ5 3=IRQ3 4=IRQ7 5=IRQ11 6=IRQ12 7=IRQ15 ___________________________________________________________________________ 22 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Bits 5-3 Channel 2 (MIDI) IRQ selector 0=No Interrupt 1=IRQ2 2=IRQ5 3=IRQ3 4=IRQ7 5=IRQ11 6=IRQ12 7=IRQ15 Bit 6 1 = Combine Both IRQS using Channel 1's IRQ Bit 7 Reserved (Set to 0) Note: If the channels are sharing an IRQ, channel 2's IRQ must be set to 0 and turn on bit 6. A bus conflict will occur if both latches are programmed with the same IRQ #. DMA control register I/O W 2XB (2X0- bit 6 = 0) Bits 2-0 DMA Select Register 1 0=NO DMA 1=DMA1 2=DMA3 3=DMA5 4=DMA6 5=DMA7 Bits 5-3 DMA Select Register 2 0=NO DMA 1=DMA1 2=DMA3 3=DMA5 4=DMA6 5=DMA7 Bit 6 - Combine Both on the same DMA channel. Bit 7 - Reserved (Set to 0). Note: If the channels are sharing an DMA, channel 2's DMA must be set to 0 and turn on bit 6. A bus conflict will occur if both latches are programmed with the same DMA #. C programmers can refer to the UltraSetInterface routine in INIT.C of the lowlevel source code for the proper sequence for programming these latches. If the order is not right, unpredictable things may happen. Changing the IRQ settings will usually cause an IRQ on the OLD IRQ because it is no longer being driven low by the latches and it will tend to float up. That low to high transition causes an IRQ on the PC. Normally, this is not a problem, but it is something to be aware of. ___________________________________________________________________________ 23 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2.15 Register Control - 2XF This register is only valid for UltraSound boards that are at or above revision 3.4. It is not valid for boards which have a prior revision number. On 3.4 and above boards, there is a bank of 6 registers that exist at location 2XB. Register 2XF is used to select which one is being talked to. Register # Use 0 Same as pre-3.4 boards 1-4 Unused - Reserved 5 Write a 0 to clear IRQs on power-up 6 'Jumper register' Jumper register definition: ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Reserved (Set to 0) | | | | | | +-------- 1=Enable MIDI port addr decode | | | | | +------------ 1=Enable joystick port decode | | | | +---------------- Reserved (Set to 0) | | | +-------------------- Reserved (Set to 0) | | +------------------------ Reserved (Set to 0) | +---------------------------- Reserved (Set to 0) +-------------------------------- Reserved (Set to 0) 2.16 Volume ramping description The UltraSound has built-in volume ramping to facilitate the implementation of the attack decay sustain release envelopes. This ramping uses the same principle as the voices for updating the volume values. A section of the envelope can be programmed such that the PC does not need to be burdened with the task of changing each volume at specified intervals. At the end of that particular section, an IRQ can be generated so that the next section can be programmed in. This continues until the entire envelope has been completed. The start and end points as well as the increment rate are fully programmable. The hardware volume registers and bit definitions are: Current Volume (9,89) EEEEMMMMMMMM (Bits 15-4) Volume Start (7,87) EEEEMMMM (Bits 7-0) Volume End (8,88) EEEEMMMM (Bits 7-0) ___________________________________________________________________________ 24 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Volume Incr (6,86) RRMMMMMM (Bits 7-0) Once the current volume, start and end volumes are programmed, the only thing left is to set up the volume increment register. This register determines how fast the ramp takes place and with what granularity. The finer the granularity, the smoother (but slower) the ramp. The increment register has 2 fields. The first is the amount added to (or subtracted from) the current volume to get to the next one. These are the low 6 bits and can range from 1 to 63. A 1 is a long, slow ramp compared to a 63. The upper 2 bits determine how often the increment is applied to the current volume. The rate bits are defined as: Rate Bits Volume Update Rate 00 FUR (FUR = 1/(1.6*#active voices)) 01 FUR/8 10 FUR/64 11 FUR/512 Each rate increment is 8 times longer than the preceding one. This means that the value to store for the fastest possible ramp is 1Fh (63), and the value for the slowest possible ramp is 1Ch (193). The approximate times for a full scale volume ramp (0-4095) are: Rate Vol Inc 14 Voices 32 Voices ---- ------- --------- --------- 0 63 1.4 ms 3.3 ms 0 1 91.7 ms 209.7 ms 1 63 11.5 ms 26.2 ms 1 1 733.8 ms 1.7 sec 2 63 91.8 ms 209.7 ms 3 1 5.9 sec 13.4 sec 3 63 734.0 ms 1.7 sec 3 1 47.0 sec 107.3 sec Note that these times are for full sweep ramping. Since a volume ramp usually goes between points in between the limits, the actual ramp times will be much smaller. Allowing to let the volume ramps to go to the extremes can cause a random oscillation of the volume when it reaches the limits. This is caused by an overshoot past the limit due to a large step size. The SDK routines protect against this by limiting how close to the rails you can get. ___________________________________________________________________________ 25 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Chapter 3 - Programming the UltraSound 3.0 Introduction The UltraSound is frequently referred to as 'GUS', for Gravis' UltraSound. The card has a very good reputation among programmers, since it is very easy to program using a high level language, and provides incredible sound. However, there are some things that you need to be aware of to successfully program the board. Users of either language should consult their respective example programs before attempting to use the card. Also, C users should consult section 3.12, while Pascal users should consult section 3.13. 3.1 Sound A sound is something that you hear. It is produced by a source and received by the ears of the listener. There are several mediums for the storage of sound, including vinyl, magnetic surfaces, and more recently optical disks. When something makes a sound, it creates waves of different air intensity. These waves are registered as 'sound', and the amplitude and frequency of these waves determine the 'type' of sound. Amplitude is determined by the highest and lowest points of the wave, and is registered by your ear as the volume of the sound. Therefore, the loudness of a sound is changed by varying the amplitude of the sound's wave. Frequency is determined by the number of pulses (passes of a single wave peak and trough) which pass a certain point in a certain amount of time. The measure of sound frequency is done using the standard measure for frequency - Hertz (Hz). The higher the frequency, the higher the pitch of the sound that is heard. Normal sound like speech and sound that comes from mixes of different instruments is a very complex pattern of changes in amplitude and frequency. To record the wave's pattern, a computer takes a large number of 'samples' of the wave produced by the sound. Then when it goes through the individual 'samples' at the same rate they were recorded, it can reconstruct the original wave and produce a sound which is close to the original sound. If the sample rate is not high enough, the computer can miss changes in the amplitude and frequency, and the sound reproduced will not sound exactly like the original sound. The number of samples taken each second is referred to as the 'sampling rate', and the number of samples played back each second is referred to as the 'playback rate'. When a rock band plays a song, the combined sound wave produced by the singers and instruments is produced. To record this wave at CD-quality on a computer medium (like memory or a hard disk), the wave is 'sampled' 44100 times each second. Since the number of samples per second is a frequency, this rate is measured again in Hertz. Therefore, we say that the recording frequency is 44.1 kHz. ___________________________________________________________________________ 26 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ If I record a sound at 44.1 kHz for two seconds, I will have 88200 individual 'samples'. The data that comprises these 'samples' is typically referred to as a 'sample' itself. When a recording made at 44.1 kHz is played back at 44.1 kHz, the original sound is reproduced almost exactly. Note that the recording and playback frequency are different from the frequency of the sound itself. Most other sound cards do not have any RAM on them, and therefore have to use the PC's RAM to store the sounds they produce. This leaves less room for application programs, and is just another thing that the CPU has to track and process - slowing down the machine. On the UltraSound, samples are stored on the card in it's DRAM. This leaves more space in PC RAM for programs. Also, the GF1 chip in the UltraSound can play 32 independent sounds at once without using any CPU time - so the machine doesn't slow down at all! 3.2 The Basics of the UltraSound The only things you need to know to set up the card when your program starts is the maximum # of voices you are going to need and the maximum playback frequency you're going to need for any sample. Consult the frequency table in section 1.3 to see how many voices you should initialize the card with. If you don't need a large number of voices, initializing the card to use fewer voices means that some 'oversampling' will occur, making the sound better in quality. You cannot initialize for fewer than 14 voices or larger than 32. To open the card, you call the UltraOpen function. This will set up the interrupt vectors and control procedures for the card, and then reset the card to use the number of voices you specify. When you are finished with the card, you need to close it so that the interrupt procedures, voices, and volumes get stopped and reset to 'normal'. To do this, call the UltraClose function. Please see chapter 4, the reference guide, for more information on the syntax and usage of these routines. 3.3 Using GUS Memory The UltraSound has a default amount of 256K of memory on the card that can be upgraded in 256K increments up to 1 MB. If you are developing for the card, you may want to upgrade to a full megabyte. Also, you should be aware that many users of the card do NOT have 1 MB of RAM installed, and that if you are going to load samples into the card's memory, you need to check to make sure you have enough room. The function UltraMaxAlloc (additionally for Pascal users the functions UltraMemAvail and UltraMaxAvail) will tell you how much RAM you still have available. Because of the way the GUS memory is structured physically, you cannot allocate chunks larger than 256k at one time. ___________________________________________________________________________ 27 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ The C and Pascal versions of the SDK use different methods for keeping track of blocks of memory that have been allocated and blocks that remain free. The C version uses a small portion of GUS RAM to keep track of the blocks, whereas the Pascal version uses a small amount of normal heap space. In any event, you do not have to worry about the internal operation of these routines. To see how the Pascal version uses a small amount of heap space to manage GUS memory, see section 3.13.3. The memory allocation routines will ONLY return 32 byte aligned addresses, so if your application uses them, this will not be a problem. If you chose not to use the allocation routines provided, be sure and follow these rules. To obtain a legal block of UltraSound RAM that is guaranteed to be aligned on a 32-byte boundary, use the UltraMemAlloc function. To free up the memory after you have used it, use the UltraMemFree function. Once again, see chapter 4, the reference guide, for more information on the syntax and usage of these routines. You DO NOT necessarily need to use the memory allocation and deallocation routines to program the card. You DO have to be careful if you choose to do without them. The DMA can only begin on a 16 or 32 byte boundary. An 8 bit DMA channel (0-3) must start on a 16 byte boundary while a 16 bit DMA channel (4-7) must start on a 32 byte boundary. If an improper address is supplied, the address will be truncated down when the DMA occurs. A DMA to or from the card cannot cross a 256K boundary. To send data from PC memory to the card, use the UltraDownload function. To read data from the card into PC memory, use the UltraUpload function. If you download data to the card, ensure that if the data is not in two's compliment form you convert it to two's compliment on the download. To do this, use the DMA control bits (see Appendix D). Any data you Upload from the card that is playable will be in two's compliment format. 3.4 What are Samples? As mentioned above, a 'sample' is raw data that has been recorded using the GUS or another digital sound recording device. Most of these files will have extensions of .SND or .WAV. There are other extensions available, but these are the most common. A technique known as 'frequency shifting' is sometimes used to produce alternate 'notes' of a specified instrument. Amiga '.MOD' file players use this technique to make a single instrument sample sound differently for each note played over 3 octaves. Essentially, the technique utilizes the 12-note scale with the principle that each half-step is 1/12 root of 2 times the value of its predecessor in frequency (working from left to right). For instance, if we take C as (1*frequency), C# would be (1.05946*frequency). Since there are 12 notes in an octave, when you reach B (the end of the octave), the next note is (2*frequency) - so doubling the original frequency means you raise the note one octave. ___________________________________________________________________________ 28 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ There is a drawback to the technique mentioned above. If a sample was recorded at 11kHz for two seconds, and then played back at 22kHz to raise the note one octave, it would only play for one second instead of two. This is because you have (11025 * 2 seconds) = 22050 bytes of data. If you play this at 22050 Hz, or 22050 bytes per second, you will have one 1 second of sound. Thus, if you raise or lower the frequency in this manner, the sound will respectively take a shorter or longer amount of time to play. A common problem with samples is that the start and/or end of them is at a reasonably high amplitude. When the sample starts and/or stops, the values flowing through the DACs on the card change suddenly. This usually results in an audible 'click' or 'pop' through the speakers. Section 3.8 looks at the removal of this and other types of clicks and pops than can occur. 3.5 Using Voices Depending on the number you choose when opening (or initializing) the card, you can use anywhere from 14 to 32 voices at once. You can look at the voices as those of the people in a choir. You can tell the voices to start or stop singing (UltraStartVoice, UltraStopVoice), sing at a specified pitch or frequency (UltraSetFrequency), loudly or softly (UltraSetVolume, UltraSetLinearVolume), and at a specified balance (UltraSetBalance). The exciting thing about the voices is that any number of them can be played at once, and there is no extra drain on the CPU if you are playing a larger number of voices as opposed to a smaller one. Also, these voices can play any type of sampled sound, 8- or 16-bit, at frequencies up to 44.1 kHz. Any number of voices can even play the same sample at the same time. To start up a voice, you use the UltraStartVoice or UltraPrimeVoice/ UltraGoVoice combination. Either one of these two routines will ask for a voice number, as well as a begin, start, and end location. The reason begin and start locations are both asked for is in case you wish to play a sample backwards or loop at a specific position which is not the start. The following diagram may make this clearer: Forward playing: ================ -------->------->-------->------| ^---------<------<-| Begin ...... Start Loop......... End Loop location ... location ......... location Backward playing: ================= |------<-----<------<------<---------- |--------->----->---->---^ End Loop.... Start Loop............... Begin location .... location ................location ___________________________________________________________________________ 29 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ A voice starts at the 'begin' location, and continues playing until it hits the 'end' location. If the 'begin' is a greater location than the 'end', the sample will be played backwards. The 'start' location is the start of the loop if looping is enabled for the voice begin played. Depending on whether and how you wish to loop the voice, you can make various effects occur. Looping of the voices is discussed in the next section. When you have finished with a voice, You should stop it (UltraStopVoice), and deallocate it (UltraFreeVoice). Please see section 3.8 on click removal for information on problems with stopping a voice abruptly. 3.6 Volumes A volume is available for each voice. If the volume for a voice is not set to zero, the data at the position of the voice's accumulator will be summed into the final output. Therefore, it is important that if you are not using a voice you should set it's volume to zero. The UltraSound uses logarithmic volumes using an exponent and a mantissa. For detailed information about how the volumes work, please see section 2.16. As an alternative to using the logarithmic units, a table has been provided in the lowlevel code which gives linear equivalents of the full volume range. You can therefore use the logarithmic volume routines or the linear volume routines - whichever your application requires. Volumes for each voice may be 'ramped'. This means that you can specify a start and end volume, and the card will smoothly change the volume from the beginning level to the end level. In ramping the volumes, a 'rate' must be specified which assigns how fast the volume is changed. Please see section 2.16 for detailed information on how to set up a volume ramp rate. 3.7 Using Looping You can loop both voices and volumes on the UltraSound. Looping a voice can result in sustaining a particular note, the generation of a unique sound, or any number of other 'special effects'. Looping the volume can result in a tremelo effect at various intervals. Looping a sample is a simple task of setting the mode bits when specifying the start of a voice (through UltraPrimeVoice, UltraStartVoice), or when using UltraSetLoopMode. See appendix C for a definition of which bits in the mode byte control looping. Also, Pascal users should be aware of the constants that are available for use instead (see section 3.13). Looping the volume is essentially as simple as looping a voice. When specifying the volume for a particular voice (using UltraSetVolume or UltraSetLinearVolume), you set the volume mode byte depending on how you wish to loop the volume. See appendix B for a definition of which bits in the mode byte control looping. ___________________________________________________________________________ 30 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 3.8 Clicks and click removal As was mentioned in a few of the preceding sections, 'clicks' and 'pops' can occur when the output of the DAC changes suddenly. This is the most frequent type of click, since there may not be a way to control the data values the voices are trying to play. In general, it is necessary to remember that all voices are being summed in to the final output, even if they are not running. This means that whatever data value that the voice is pointing at is contributing to the summation. It is important that a voice be pointed to a known value at a known location after it is stopped so that some control is kept over it. For instance, if a voice were left at whereever the end position was for the last time it played, a pop could occur if new data were either DMA'ed or poked over the top of it. For this reason, it is recommended that a voice be pointed to a location containing a 0 and that its volume be set to 0. At that point, the voice will have no contribution to the output. There are some particular cases where clicks most frequently occur: During Reset - Because of the way the card is reset, a pop can occur through the speakers when a reset occurs. The way to remove this pop is to disable the output, reset the card, and then enable the output again. During a balance sweep - Since there are only 16 pan positions and there is such a large jump between individual positions, a relatively fast balance sweep from one side to another may produce clicks. You can get a very smooth balance sweep using 2 voices and volume ramping. Set one voice up to one side and one to the other, and ramp one down from volume X to zero at the same rate as you ramp the other from 0 up to volume X. The result is a very smooth balance sweep. When starting and stopping a voice - By setting up fairly fast rates when a sample start or ends and ramping up or down appropriately, any pop created by a sudden change in the DAC value will be summed in at such a low volume, it will never be heard. End points not set properly - Make sure your end points are at the ends of your samples. It is a very common mistake to set an end point to 1 sample beyond the end. For example, if a sample is 100 bytes long an starts at location 100, the end point is at position 199, NOT position 200. Also, it is possible that the GF1 will interpolate data at points beyond the end point. To ensure that this does not cause 'clicks', use a few extra bytes after the end point of the sample to maintain it. Loop points not set properly - The same problem as stated above (end points) is common for loop start and end points. Be sure that the data at the end of the loop and the beginning of the loop are practically identical, since if there is a large step between them a click will result because the DAC value will change suddenly. ___________________________________________________________________________ 31 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 3.9 Interrupt Handling Functions There are 9 functions (procedures for Pascal users) which can be used to define a handler for events that happen under interrupt on the UltraSound. These are NOT interrupt handlers but are callbacks from the interrupt handler. This means that they should not be defined as interrupt type functions but must adhere to the general rules of interrupt code. No DOS calls should be made and care must be taken not to cause problems with code running in the foreground. UltraDramTcHandler UltraRecordHandler UltraMIDIXmitHandler UltraMIDIRecvHandler UltraTimer1Handler UltraTimer2Handler UltraWaveHandler UltraVolumeHandler UltraAuxHandler All these routines return the old callback address so chaining could be done if desired. This not usually necessary. It is also not necessary to restore the handler back to the old one when exiting your application. However, the UltraClose function MUST be called to restore the actual interrupt handler. These will only be CALLED if the interrupt for the particular function is enabled in the mode parameter (see the Appendices for the bit definitions.) It is also not necessary to set up a callback since it has a default associated with it. You should be able to make any SDK library calls from within the interrupt handlers. All the library functions protect themselves from being interrupted while doing critical operations. 3.10 Rollover feature Each voice has a 'rollover' feature that allows an application to be notified when a voice's playback position passes over a particular place in DRAM. This is very useful for getting seamless digital audio playback. Basically, the GF1 will generate an IRQ when a voice's current position is equal to the end position. However, instead of stopping or looping back to the start position, the voice will continue playing in the same direction. This means that there will be no pause (or gap) in the playback. Note that this feature is enabled/disabled thru the voice's VOLUME control register (since there are no more bits available in the voice control registers). A voice's loop enable bit takes precedence over the rollover. This means that if a voice's loop enable is on, it will loop when it hits the end position, regardless of the state of the rollover enable. A simple example of this technique is: 1) Allocate a chunk of DRAM: 20K, for example. 2) Load the entire 20K with wave data. 3) Start up a voice with looping disabled and rollover enabled. Set its end position to the MIDDLE of the buffer. ___________________________________________________________________________ 32 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 4) When the voice hits the middle, you will get an IRQ, but the voice will continue to play. 5) At this point, enable looping and disable the rollover. Also, set the end position to the end of the buffer. This will make the voice loop back to the beginning without stopping. 6) Now load the FIRST 10K with more wave data. This will make sure that there is correct data to play when the voice loops. 7) When the voice loops, you will get and IRQ. 8) At this point, disable looping, enable rollover and set the end position back to the middle of the buffer. 9) Now load the SECOND 10K with more wave data. This will make sure that there is corect data to play when the rollover occurs. 10) Continue in a loop, starting at step #4. Note:This algorithm does not take care of an initial condition where not enough data exists to fill one complete buffer. It also doesn't address how to finish playing the last incomplete buffer. These points are left up to your implementation to resolve. 3.11 Stereo playback Stereo data is represented in an interleaved format, meaning that the data alternates between a sample for the left channel and a sample for the right channel. It is either the responsibility of the hardware or the software to ensure that the data gets used properly. Stereo data can be played back two completely different ways, either under software control or hardware control: Software Control ================ This is the most flexible method, since it allows for playback at any frequency. The basic algorithm is to de-interleave the data into a left channel buffer and a right channel buffer, then send the data to two independent voices on the card. Here is a brief description of the algorithm that would be used: 1) Allocate 2 voices. One for left and one for right. 2) Set the left voice pan position all the way left. 3) Set the right voice pan position all the way right. 4) Set both voice volumes to the same value. 5) Allocate 2 chunks of UltraSound DRAM of the same size, say 20K apiece. 6) Read in 40K of stereo data. 7) De-interleave data into 20K of left data and 20K of right data. 8) DMA left buffer into DRAM buffer #1. 9) DMA right buffer into DRAM buffer #2. 10) Prime right voice to loop on all 20K of data forever. No IRQ. 11) Prime left voice to play 10K of its data with the rollover feature enabled (See discussion on seamless digital playback above). 12) Start both channels up as close together as possible so they track together. The right channel will not be touched, except to DMA new data into its buffer. ___________________________________________________________________________ 33 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 13) At this point the software will loop on the following steps until the data is exhausted. The rollover will allow the software to play half the buffer and get notified when it has finished, but the playback will continue uninterrupted. When the rollover IRQ happens, the left voice will be changed to loop at the 20K position. At this point the software will be able to load the next 10K of left channel data into the FIRST 10K of the left channel DRAM. The right channel is also sent 10K of data into its FIRST 10K. 14) Now each channel has the data to play when they loop at their 20K marks. 15) When the left channel's loop IRQ happens, both the left and right channel can be sent more data into their SECOND 10K buffer. Also, the left channel's looping will be turned off and it will be told to rollover at its 10K mark again. Now both channel's are ready to play thru their 10K marks with no interruption. 16) Go back to step 13 and loop The above algorithm does not detail the startup conditions or the shutdown conditions. Things that you will need to accomodate are: 1) Less than 1 buffer full of data. 2) How to terminate when data ends in first buffer. You must allow it to loop back from the end and then stop at the end of the data instead of rolling over at the middle. Both right and left must be taken care of. 3) How to terminate when data ends in the second buffer. After filling in the data in the second buffer, you can turn off the rolling over at the middle and tell it to stop at the end of the data in the second buffer. Be sure you understand all the starting and ending possibilties when coding you application. The basic idea is to let the right voice loop forever on a buffer of data and feed it data based on IRQ generated from the left channel. The left channel can be monitored via the rollover feature to generate and IRQ at the halfway point and an IRQ at the loop point at the end of the buffer to know when the second buffer finishes. Data will then be sent to BOTH voices for the portion of data that it just finished playing. Hardware Control ================ This method has the advantage of not needing to de-interleave the data before DMAing it into UltraSound DRAM: the data is sent to DRAM in the interleaved format. This means that a voice must be able to play every other sample to correctly separate the right and left channel. It is possible to set up the GF1 and the 2 voices to do this, however, it will only work for 2 usable freqencies. (44100 and 22050). Any other frequencies must be done using the software control de-interleaving method. ___________________________________________________________________________ 34 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ The theory behind how to achieve this is quite complex. It involves using the # of active voices and the voice's frequency control register to get it to grab the correct samples at the proper frequency. Here is the setup necessary to playback 44100 Hz stereo and 22050 Hz stereo data: 44100 Hz:Set the # of active voices to 14 and set both voices playback rate to 88200 hz. 22050 Hz:Set the # of active voices to 28 and set both voices playback rate to 44100 hz These two configurations will make both voices have a frequency control register equal to 2. The frequency control register holds the amount added to the current voice playback location to get the next playback location. Normally, this number is a fraction so interpolation is done to generate intermediate points between successive locations. With interleaved data, we don't want to interpolate since the neighboring data points belong to the other channel. We want to completely skip over the point after the current one, and go to our current position plus 2. The number of active voices selected will determine the update rate. The more voices that are active, the longer time between updates, thus slowing down the frequency. 14 active voices will give and update rate of about 22.5 microseconds (44100 hz), whereas 28 voices will give and update rate of about 45 microseconds (22050 hz). The intialization of the voices in this mode will obviously be different. Essenntially, the voices must be set up to play the same data but offset their start and end points by one or two bytes, depending on whether it is an 8- or 16-bit sample. 3.12 C-specific information Currently, 10 different C memory model/compiler configurations are supplied. Large, medium, small and tiny models for Borland and Microsoft compilers are available, as well as flat model for WatCom and MetaWare compilers. The level 0 library contains the lowest level function calls that talk directly to the hardware. The level 1 library is a little higher and contains functions that call level 0 functions. The 3D libraries contain the functions necessary for implementing the Focal Point 3D sounds. Borland C++ 2.0 and 3.1, Microsoft C 6.0, Watcom C9.0/386, and Metaware High C/C++ were used to test the library routines. These are the naming conventions for the C libraries: ultra0lb.lib Level 0, large model, Borland C ultra1lb.lib Level 1, large model, Borland C ult3D_lb.lib 3D, large model, Borland C ultra0mb.lib Level 0, medium model, Borland C ultra1mb.lib Level 1, medium model, Borland C ult3D_mb.lib 3D, medium model, Borland C ___________________________________________________________________________ 35 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ ultra0sb.lib Level 0, small model, Borland C ultra1sb.lib Level 1, small model, Borland C ult3D_sb.lib 3D, small model, Borland C ultra0tb.lib Level 0, tiny model, Borland C ultra1tb.lib Level 1, tiny model, Borland C ult3D_tb.lib 3D, tiny model, Borland C ultra0lm.lib Level 0, large model, Microsoft C ultra1lm.lib Level 1, large model, Microsoft C ult3D_lm.lib 3D, large model, Microsoft C ultra0mm.lib Level 0, medium model, Microsoft C ultra1mm.lib Level 1, medium model, Microsoft C ult3D_mm.lib 3D, medium model, Microsoft C ultra0sm.lib Level 0, small model, Microsoft C ultra1sm.lib Level 1, small model, Microsoft C ult3D_sm.lib 3D, small model, Microsoft C ultra0tm.lib Level 0, tiny model, Microsoft C ultra1tm.lib Level 1, tiny model, Microsoft C ult3D_tm.lib 3D, tiny model, Microsoft C ultra0wc.lib Level 0, Flat model, Watcom C ultra1wc.lib Level 1, Flat model, Watcom C ult3D_wc.lib 3D, Flat model, Watcom C ultra0mw.lib Level 0, Flat model, Metaware High C/C++ ultra1mw.lib Level 1, Flat model, Metaware High C/C++ ult3D_mw.lib 3D, Flat model, Metaware High C/C++ Several example applications are supplied on the toolkit disks to show you how to interface to the libraries. Please look them over carefully. They are the best way to get a handle on the way the card operates. 3.13 PASCAL-specific information Version 2.10 of the UltraSound SDK is the first version which has full support for Turbo Pascal 6.0 and 7.0, as well as Borland Pascal 7.0. For those of you who are curious, Borland Pascal 7.0 is the professional version of Turbo Pascal 7.0. It costs more, but includes a lot of bells and whistles that are not included in the regular 'Turbo' version (for more information, contact Borland). The translation was done entirely by Kurt Kennett of Ingenuity Software, and is a direct translation of the C toolkit. The code was written in Borland Pascal 7 and then minor adjustments were made to make it compile under Turbo 6 and 7. ___________________________________________________________________________ 36 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Due to the fact that the Pascal translation was done after the C code was finished, some of the naming conventions for some of the functions and record types reflects a 'C-like' syntax and style. Some conversion of function result types has been done to reflect a more Pascal-like structure. After installation of the SDK, there will be two units and one interface file available in the PASCAL subdirectory: ULTRADRV.V60 Turbo Pascal 6.0 version ULTRADRV.V70 Turbo/Borland Pascal 7.0 version ULTRADRV.INT This is the interface section for all units. You should copy the unit you wish to use to one of your unit directories, and then rename the unit to 'ULTRADRV.TPU'. You may experience compiling problems if you do not rename the file. You may also wish to copy the interface file to use as a quick-reference. For users of Turbo and Borland Pascal 7.0, a help file has been provided to allow quick context-sensitive help from inside the IDE editor. Also, a MAKEHELP.TXT version of this help file has been provided for those users who employ TurboPower's context-sensitive help system. To install the IDE help file, load Turbo or Borland Pascal 7 and select the 'Help' menu from the menu bar. Choose the 'Files...' option, and select the 'Add' button. Choose the file 'ULTRADRV.TPH' from the UltraSound PASCAL subdirectory. After you have done this, you may use the F1 key and it's derivatives to access reference information for any of the functions and procedures in the SDK reference manual (chapter 3). For example, after installing the help file, type the word 'UltraSetFrequency' into the IDE, move the cursor on top of the word, and press CTRL-F1. You will be given an explanation of the syntax and usage of the Procedure, as well as information on related procedures, functions, constants, and variables. A short section detailing specific information about types and constants available follows. For more information, please see the interface file ULTRADRV.INT. 3.13.1 Available constants and variables The SDK TPU (or 'driver') has some predefined constants to assist you in programming various aspects of the card. There are three common frequencies that sampled sounds are played at: 11, 22, and 44 KHz: Khz_11 = 11025; Khz_22 = 22050; Khz_44 = 44100; ___________________________________________________________________________ 37 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Whenever volume is specified, there is a volume control byte. The bits in this byte control several important functions. OR the following constants together with zero if you need them. If you don't, just specify zero as the volume control byte: Loop_Volume = 08; Bi_Directional_Volume = 16; Enable_Volume_Handler = 32; Whenever a voice is started or changed, there is a voice control byte. The bits in this byte control several important functions for each voice. OR the following constants together with zero if you need them. If you don't, just specify zero as the voice control byte: Voice_Data_16Bit = 04; Loop_Voice = 08; Bi_Directional_Voice = 16; Enable_VoiceEnd_Handler = 32; When a DMA transfer is started, you need to tell the DMA controller what type of data you are sending to the UltraSound, as well as if the controller should convert the data to 2's complement. If you are downloading ram .SND files to the card, you need to OR the Convert_Data constant to 0 as the control byte. If you are downloading 16 Bit data, you need to OR the control byte with DMA_Data_16Bit. DMA_Data_16Bit = 64; Convert_Data = 128; There are several predefined variables to help you manage the UltraSound card as you use it. These variables determine the Configuration, error codes, and whether or not the card is installed: Ultra_Installed : BOOLEAN; If the driver cannot read the environment variable 'ULTRASND' when it is initialized (i.e. when your program starts), this BOOLEAN will remain false. Otherwise, the variable Ultra_Config (see below) will be set up with the values found in the environment variable. Ultra_Config : Ultra_CFG; If the driver was able to read a configuration from the 'ULTRASND' environment variable, this variable record will be filled in with the appropriate information found in the environment. If the environment string is not found, this variable will assume a default configuration. All of the functions and procedures in the driver use this configuration to initialize and use the card. Be careful if you're going to go sticking values into this record. The main use of this variable is to display the active configuration to the user. ___________________________________________________________________________ 38 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ The driver predefines three variables to help you with error control: UltraOk : Boolean; This boolean always maintains the status of the last UltraSound procedure or function. If there was a problem with the routine, this variable will be FALSE. Otherwise, it will be TRUE (indicating no error). UltraError : Integer; This is the "Error Code" of the Error. This variable is set to 0 if no error has occurred, and is only set when UltraOk becomes FALSE. UltraErrorStr : String; This is the "description" of the error which has occurred. As an added bonus to you, the programmer, this string is set with a (descriptive) error message whenever a card-related error occurs. 3.13.2 Examples There are several example programs included with the units described in the previous section: GUSINIT.PAS This example file is a small unit that you may want to use to initialize the GUS before you use it. The important thing about this unit is that it installs an 'Exit Procedure' which is called upon program termination (normal or crash-out). The exit procedure closes down the card before returning to DOS (or the IDE, depending upon where you are). In TP and BP 7, you can type 'ExitProc' into the IDE, move the cursor on top of it, and hit CTRL-F1 for a detailed explanation of what an exit procedure is. After including this unit in your 'uses' clause, your main program can just go ahead and start using it. You don't need to call UltraOpen, UltraReset, or even UltraClose when you're finished. GUS1.PAS This is the primitive example program for the card which loads three sounds (a laser blast, a photon torpedo, and a missile) into the card's RAM and then allows you to press the '1'..'3' keys to play them. This example uses the GUSINIT unit - showing you how easy it is to use. LOADMOD.PAS This is a unit which provides services to load Amiga '.MOD' files into GUS RAM and main memory. After calling a single function, the file will have been loaded and the samples contained in it set up to play. It is left up to you to supply playback routines or trivial sample-playing routines. ___________________________________________________________________________ 39 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 3.13.3 Management of GUS RAM. The Pascal version of the SDK employs a different approach to memory control than the C toolkit. In the C SDK, a small amount of the GUS RAM is used to keep track of allocated blocks. In the Pascal SDK, a very small amount of heap space is used to keep track of the blocks. Because of the way the UltraSound RAM is structured, any particular card has a total amount of installed RAM that is a multiple of 256k. Since samples cannot be played across these boundaries, the driver unit divides the total RAM by 256k into 1 to 4 'pools'. |--------------------------------------| | | 256k (a single 'pool') |--------------------------------------| Each of these pools is seen as a single unit which can be subdivided. For the purposes of this example, we will use a GUS which has 512k installed. If this is the case, there will be two pools of RAM available: |--------------------------------------| | | Pool 1 |--------------------------------------| | | Pool 2 |--------------------------------------| Once the card has been initialized, there will be a small amount of heap space being used to keep track of these pools (usually less than 100 bytes). At this point, the UltraMaxFree function will return 256k, since the size of the largest block that can be allocated is one entire pool. The UlraMemAvail function will return 512k however, reflecting the total amount of memory still available. If a user were to allocate a 128k chunk of memory, the allocation routines would look in each pool sequentially until a pool is found with enough room for the new block. At this point in our example, it would use Pool 1. |----------------------------------------| |XXXXXXXXXXXXXXXXXXXX | Pool 1 |----------------------------------------| | | Pool 2 |----------------------------------------| The X's in the diagram above reflect used space. If the size of the block being allocated is not divisible by 32, the actual size of the block in memory is increased to the next multiple of 32. For example, if I were to ask for a 50 byte chunk of RAM, the actual size of ram that I would be using would be 64 bytes in size. This is so that all memory locations returned will be aligned on a 32-byte boundary. At this point, the UltraMaxFree function will return 256k, since the size of the largest block that can be allocated is still one entire pool (Pool 2). The UltraMemAvail function will return 384k, reflecting the total amount of memory still available. ___________________________________________________________________________ 40 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ If a user were to now allocate a 200k chunk of memory, the allocation routines would once again look in each pool sequentially until a pool is found with enough room for the new block. At this point in our example, it would have to use Pool 2, since there is not enough room left in Pool 1. |----------------------------------------| |XXXXXXXXXXXXXXXXXXXX | Pool 1 |----------------------------------------| |YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY | Pool 2 |----------------------------------------| At this point, the UltraMaxFree function would return 128k, since the largest block free is in Pool 1. UltraMemFree would now return 184k. If we were to allocate a third block of 70k, the diagram would look like: |----------------------------------------| |XXXXXXXXXXXXXXXXXXXXZZZZZZZZZZ | Pool 1 |----------------------------------------| |YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY | Pool 2 |----------------------------------------| If we were to now DEallocate the first block (the X's in the diagram), we would be left with a 'hole' in the ram. This is reminiscent of a hard disk's free space fragmentation. |----------------------------------------| | ZZZZZZZZZZ | Pool 1 |----------------------------------------| |YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY | Pool 2 |----------------------------------------| Note that the SDK's memory allocation routines do NOT relocate samples to fend off fragmentation. Note also that the memory routines are set up and are ready to be used as soon as you call UltraOpen, and are closed down automatically (and the heap space they use deallocated) when you call UltraClose. 3.14 Coming Attractions There are a couple of addendums to the SDK that will be released as they are completed. They deal with hardware that is in the process of being implemented. Code and documentation necessary to make them operate will be made available as soon as possible. 1) New mixer. All UltraSounds that are made that have a board revision number greater than 3.6 will incorporate a new mixer. Basically, this mixer allows software control over the levels of the input and output signals. This is a 1 chip mixer: Gravis will make specs and block diagrams available as soon as we can. ___________________________________________________________________________ 41 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 2) 16 bit recording support. This falls into 2 categories: daughter card support and UltraMax support. Both of these use the same chip (Crystal Semiconductors CS4231 CODEC). Contact Crystal to get a data sheet for specs on how to program it: their telephone number is (512) 445-7222. The only difference in the 16 bit recording capabilities of the 2 cards is in how it interfaces to the rest of the UltraSound card. Block diagrams for both of these boards will be made available when the toolkit for the codec is released. 3.15 Technical Support Questions regarding the SDK can be sent to: SDK Technical Support Advanced Gravis 101-3750 North Fraser Way Burnaby, British Columbia V5J 5E9 FAX (604) 431-5155 If you have access to a modem, you can contact Gravis' BBS at: (604) 431-5927 If you have access to internet mail services, you can send mail to 'john.smith@gravis.com' for general questions, or 'kurt.kennett@gravis.com' for questions regarding the Pascal version of the SDK. Please make sure you have consulted this documentation before contacting Technical support. (RTFM!) ___________________________________________________________________________ 42 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Chapter 4 - Reference Guide This chapter is a reference guide for the routines that compromise both the C and Pascal versions of the SDK. Please see the table of contents to look up a particular routine quickly by name. UltraCalcRate _________________________________ Purpose: To calculate the rate for a volume ramp. C: unsigned char UltraCalcRate(start,end,mil_secs); unsigned int start; unsigned int end; unsigned long mil_secs; PASCAL: FUNCTION UltraCalcRate(StartV : WORD; EndV : WORD; Mil_Secs : LONGINT) : BYTE; Remarks: This routine calculates the rate necessary to ramp the volume from StartV volume (logarithmic) to EndV volume (logarithmic) in a desired # of milliseconds. This value should be passed to UltraRampVolume. This is only an approximation. The longer the time span, the less precise the result is likely to be. Returns: A value which can be passed to UltraRampVolume or UltraRampLinearVolume. See also: UltraRampVolume, UltraRampLinearVolume UltraClose _________________________________ Purpose: To close out the UltraSound card. C: int UltraClose(void); PASCAL: FUNCTION UltraClose : BOOLEAN; Remarks: This routine should be called before your application exits. It shuts down all audio and puts the card in a stable state. It also puts the PC back to the state prior to running your application (resets interrupt vectors etc). Returns: TRUE if Close was successful, FALSE otherwise. See also: UltraOpen, UltraReset ___________________________________________________________________________ 43 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraDownload _________________________________ Purpose: Download a chunk of data into UltraSound DRAM. C: int UltraDownload(dataptr,control,dram_loc,len,wait); void *dataptr; unsigned char control; unsigned long dram_loc; unsigned int len; int wait; PASCAL: FUNCTION UltraDownLoad(DataPtr : POINTER; Control : BYTE; DRAM_Loc : LONGINT; Len : WORD; Wait : BOOLEAN) : BOOLEAN; Remarks: This routine will transfer a chunk of data from the PC's RAM to the UltraSound's DRAM. It will transfer 'Len' # of bytes from DataPtr (in PC) to DRAM_Loc (in UltraSound). If 'Wait' is TRUE, then it will wait until the transfer is complete. If 'Wait' is FALSE, it will return as soon as transfer is started. In some cases where you need to get output quickly, you can start the download and then immediately start a voice playing the data. The DMA transfer is MUCH faster than the voice playback, so it will be able to download data ahead of the playback. For obvious reasons, this will not work if you want to play the data backwards. See Appendix D for a definition of the control bits. They specify the type of data being transferred. Returns: C: ULTRA_OK if no problem. DMA_BUSY if DMA Channel is busy. PASCAL: TRUE if transfer was successful. FALSE otherwise. If unsuccessful, check UltraErrorStr for the reason. See also: UltraUpload, UltraDRAMDMABusy ___________________________________________________________________________ 44 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraDRAMDMABusy _________________________________ Purpose: Test to see if the DMA channel is busy. C: int UltraDramDMABusy(void); PASCAL: FUNCTION UltraDRAMDMABusy : BOOLEAN; Remarks: This routine will check to see if the to/from DRAM DMA channel is busy. It might be useful so your application doesn't hang around while waiting for a DMA transfer to complete. Returns: TRUE if the channel is still busy. FALSE if it's free. See also: UltraWaitDRAMDMA, UltraDownload, UltraUpload UltraGoRecord _________________________________ Purpose: To start up a pre-set record. C: int UltraGoRecord(control); unsigned char control; PASCAL: FUNCTION UltraGoRecord(Control : BYTE) : BOOLEAN; Remarks: This routine will start up a pre-primed record (done with UltraPrimeRecord). It can also be used to restart a indefinite recording process from the recording handler callback. Returns: C: ULTRA_OK if no error DMA_BUSY if the channel is busy PASCAL: TRUE if Record started ok. FALSE otherwise. If FALSE check the UltraErrorStr for the reason. See also: UltraRecordData, UltraPrimeRecord, UltraRecordHandler ___________________________________________________________________________ 45 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraGoVoice _________________________________ Purpose: To start a voice that has already been primed. C: void UltraGoVoice(voice,mode); int voice; unsigned char mode; PASCAL: PROCEDURE UltraGoVoice(Voice : INTEGER; VMode : BYTE); Remarks: This routine will start up a voice that has already been primed with set-up values by UltraPrimeVoice. This can be useful if you need to start multiple voices as close together as possible. See Appendix C for the mode bit definitions. See also: UltraPrimeVoice, UltraStartVoice UltraDisableLineIn _________________________________ Purpose: To disable line level input. C: void UltraDisableLineIn(void); PASCAL: PROCEDURE UltraDisableLineIn; Remarks: If line level input is enabled and output is enabled, the input is routed directly to the output and audio will be heard. If this is not desired, use this to disable line in. See also: UltraEnableLineIn, UltraGetLineIn UltraDisableMicIn _________________________________ Purpose: To disable microphone input. C: void UltraDisableMicIn(void); PASCAL: PROCEDURE UltraDisableMicIn; Remarks: If microphone input is enabled and output is enabled, the input is routed directly to the output and audio will be heard. If this is not desired, use this to disable microphone in. See also: UltraEnableMicIn, UltraGetMicIn ___________________________________________________________________________ 46 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraDisableOutput _________________________________ Purpose: To turn off all output from the UltraSound. C: void UltraDisableOutput(void); PASCAL: PROCEDURE UltraDisableOutput; Remarks: This routine will disable all output from the UltraSound. This can be used during recording so that the input will not be looped back to the output. It is also useful to disable output during resets since that will help eliminate 'pops' during initialization. See also: UltraEnableOutput, UltraGetOutput UltraEnableLineIn _________________________________ Purpose: To enable line level input. C: void UltraEnableLineIn(void); PASCAL: PROCEDURE UltraEnableLineIn; Remarks: Turns on the line level input. If you are not recording from the line input, it is probably not desirable to have this enabled since it will be looped back to the output (if output is enabled). See also: UltraDisableLineIn, UltraGetLineIn UltraEnableMicIn _________________________________ Purpose: To turn on the microphone input. C: void UltraEnableMicIn(void); PASCAL: PROCEDURE UltraEnableMicIn; Remarks: This routine should be called when you want to record from the microphone. It is possible to have both the microphone input enabled and line level input enabled. If you are not recording from the microphone, it is recommended that it be disabled, since it will reduce noise on the output. See also: UltraDisableMicIn, UltraGetMicIn ___________________________________________________________________________ 47 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraEnableOutput _________________________________ Purpose: To enable output from the UltraSound. C: void UltraEnableOutput(void); PASCAL: PROCEDURE UltraEnableOutput; Remarks: This routine must be called to enable any output from the UltraSound. This can be used to turn on output after muting it with UltraDisableOutput. See also: UltraDisableOutput, UltraGetOutput UltraGetLineIn _________________________________ Purpose: To return the current state of line level input C: int UltraGetLineIn(void); PASCAL: FUNCTION UltraGetLineIn : BOOLEAN; Remarks: This can be useful if you want to change the state of the line level input and then restore it back to the original state. Returns: TRUE if LineIn is Enabled, FALSE otherwise. See also: UltraEnableLineIn, UltraDisableLineIn UltraGetOutput _________________________________ Purpose: To return the current output enabled state. C: int UltraGetOutput(void); PASCAL: FUNCTION UltraGetOutput; Remarks: This can be useful if you want to change the state of the output and then restore it back to the original state. Returns: TRUE if Output is Enabled, FALSE otherwise. See also: UltraEnableOutput, UltraDisableOutput ___________________________________________________________________________ 48 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraGetMicIn _________________________________ Purpose: To return the current state of the microphone input. C: int UltraGetMicIn(void); PASCAL: FUNCTION UltraGetMicIn; Remarks: This can be useful if you want to change the state of the microphone input and then restore it back to the original state. Returns: TRUE if Output is Enabled, FALSE otherwise. See also: UltraEnableMicIn, UltraDisableMicIn ___________________________________________________________________________ 49 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraDRAMTcHandler _________________________________ Purpose: To define a callback for a DMA transfer completion. C: PFV *(UltraDramTcHandler(handler)); PFV *handler; PASCAL: PROCEDURE UltraDRAMTcHandler(VAR Handler : PFV); Remarks: This procedure defines a callback for whenever a DMA transfer to the UltraSound has been completed. The routine address passed as a parameter is set as the new handler. No parameters are passed to your new handler. Returns: For C users, this routine returns the address of the old handler. PASCAL Example: Since PASCAL cannot return function or procedure type values from functions, the equivalent routine was made into a procedure which took a single VAR parameter. Thus, when you are going to change the address of a callback routine, you put the new address into a variable, run it through the procedure, and what will then be in the variable is the address of the old handler: VAR OldProc : PFV; Procedure MyNewProc; { The new handler - defined as a normal procedure } begin ... end; ... { Set OldProc to address of new handler } OldProc := MyNewProc; { Now set the new handler and return the old one } UltraDRAMTcHandler(OldProc); ... At this point, OldProc holds the address of the old routine. Therefore, to chain to the old routine, you would simply have to call OldProc from your handler. ___________________________________________________________________________ 50 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraMIDIXmitHandler _________________________________ Purpose: To define a callback for MIDI transmit interrupt. C: PFV *(UltraMIDIXmitHandler(handler)); PFV *handler; PASCAL: PROCEDURE UltraMIDIXMitHandler(VAR Handler : WORD_PROC); Remarks: This procedure defines a callback for whenever a MIDI transmit empty interrupt occurs. This can be used to send out MIDI data under interrupt control. The routine address passed as a parameter is set as the new handler. The MIDI Status Byte is passed to the new handler defined. Returns: For C users, this routine returns the address of the old handler. PASCAL Example: See the example for UltraDRAMTcHandler. See also: UltraMIDIRecvHandler UltraMIDIRecvHandler _________________________________ Purpose: To define a callback for a MIDI receive interrupt. C: PFV *(UltraMIDIRecvHandler(handler)); PFV *handler; PASCAL: PROCEDURE UltraMIDIRecvHandler(VAR Handler : TWOWORD_PROC); Remarks: This procedure defines a callback for whenever a byte is received in the MIDI input port. This can be used to get data from the MIDI port under interrupt contol. The routine address passed as a parameter is set as the new handler. The MIDI port status and MIDI data are passed to your handler. The status bits are defined in Appendix D. Returns: For C users, this routine returns the address of the old handler. PASCAL Example: See the example for UltraDRAMTcHandler. See also: UltraMIDIXmitHandler ___________________________________________________________________________ 51 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraTimer1Handler _________________________________ Purpose: To define a callback from Timer #1. C: PFV *(UltraTimer1Handler(handler)); PFV *handler; PASCAL: PROCEDURE UltraTimer1Handler(VAR Handler : PFV); Remarks: This procedure defines a callback for whenever the UltraSound's Timer 1 'Ticks'. The routine address passed as a parameter is set as the new handler. No parameters are passed to this handler. Returns: For C users, this routine returns the address of the old handler. PASCAL Example: See the example for UltraDRAMTcHandler. See also: UltraTimer2Handler, UltraStartTimer UltraTimer2Handler _________________________________ Purpose: To define a callback for Timer #2. C: PFV *(UltraTimer2Handler(handler)); PFV *handler; PASCAL: PROCEDURE UltraTimer2Handler(VAR Handler : PFV); Remarks: This procedure defines a callback for whenever the UltraSound's Timer 2 'Ticks'. The routine address passed as a parameter is set as the new handler. No parameters are passed to this handler. Returns: For C users, this routine returns the address of the old handler. PASCAL Example: See the example for UltraDRAMTcHandler. See also: UltraTimer1Handler, UltraStartTimer ___________________________________________________________________________ 52 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraWaveHandler _________________________________ Purpose: To define a callback for an end-of-wave interrupt. C: PFV *(UltraWaveHandler(handler)); PFV *handler; PASCAL: PROCEDURE UltraWaveHandler(VAR Handler : INT_PROC); Remarks: This procedure defines a callback for whenever a voice generates a wavetable interrupt. This happens when a voice hits it's end and interrupts are enabled. It will happen even if looping is on (i.e. the voice keeps playing). The routine address passed as a parameter is set as the new handler. Normally, This procedure is used to signify that a voice is done playing. This handler can be useful for starting another voice or counting the # of times that a voice goes through a loop. The voice # that generated the interrupt is passed back to your handler. Returns: For C users, this routine returns the address of the old handler. PASCAL Example: See the example for UltraDRAMTcHandler. UltraVolumeHandler _________________________________ Purpose: To define a callback for volume ramp complete interrupt. C: PFV *(UltraVolumeHandler(handler)); PFV *handler; PASCAL: PROCEDURE UltraVolumeHandler(VAR Handler : INT_PROC); Remarks: This procedure defines a callback for whenever a volume ramp ends. The routine address passed as a parameter is set as the new handler. This routine can be used to generate a volume envelope (attack, decay, sustain, release). This is done by changing to the appropriate volume ramps in the handler to handle the next part of the envelope. The voice # causing the interrupt will be passed back to your handler. Returns: For C users, this routine returns the address of the old handler. PASCAL Example: See the example for UltraDRAMTcHandler. ___________________________________________________________________________ 53 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraRecordHandler _________________________________ Purpose: To define a callback for a DMA record complete interrupt. C: PFV *(UltraUltraRecordHandler(handler)); PFV *handler; PASCAL: PROCEDURE UltraRecordHandler(VAR Handler : PFV); Remarks: This routine is called when a buffer that was being recorded into is full. The routine address passed as a parameter is set as the new handler. Normally, this procedure would be used to let the application start up another record. A double buffering scheme could be used to record data continuously. No parameters are passed to this handler. As long as the DMA channels for recording and playback are different, the UltraSound is capable of simultaneously recording and playback. At high data rates your application may have a problem with throughput. Returns: For C users, this routine returns the address of the old handler. PASCAL Example: See the example for UltraDRAMTcHandler. UltraAuxHandler _________________________________ Purpose: To define a callback for an auxiliary interrupt. C: PFV *(UltraUltraAuxHandler(handler)); PFV *handler; PASCAL: PROCEDURE UltraAuxHandler(VAR Handler : PFV); Remarks: This handler will be called at the end of ALL interrupts that happen on the UltraSound. Its primary purpose is for use with the new UltraMax card since its shares its IRQ with the GF1. Returns: For C users, this routine returns the address of the old handler. PASCAL Example: See the example for UltraDRAMTcHandler. ___________________________________________________________________________ 54 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraMaxAlloc _________________________________ Purpose: To find the size of the largest allocatable block of UltraSound DRAM. C: unsigned long UltraMaxAlloc(void); PASCAL: FUNCTION UltraMaxAlloc : LONGINT; Remarks: This routine will return the largest block of DRAM (in bytes) that can still be allocated. This can be useful for determining whether or not a patch or sample can be loaded. The maximum size of a block is 256K. Returns: # of bytes in largest available block. See also: UltraMemAlloc, UltraMemFree, for C users UltraMemInit and for PASCAL users UltraMemAvail and UltraMaxAvail UltraMaxAvail _________________________________ Purpose: To find the size of the largest allocatable block of UltraSound DRAM. C: Use the UltraMaxAlloc function. PASCAL: FUNCTION UltraMaxAvail : LONGINT; Remarks: This routine will return the largest block of DRAM (in bytes) that can still be allocated. This can be useful for determining whether or not a patch or sample can be loaded. The maximum size of a block is 256K. This routine is included to provide consistency of naming for PASCAL Programmers. Returns: # of bytes in largest available block. See also: UltraMemAlloc, UltraMemFree, for C users UltraMemInit and for PASCAL users UltraMemAvail ___________________________________________________________________________ 55 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraMemAvail _________________________________ Purpose: To find the total amount of free UltraSound DRAM. C: This function is not available. PASCAL: FUNCTION UltraMemAvail : LONGINT; Remarks: For PASCAL users, this will return the total amount of DRAM still available for use on the UltraSound. Returns: # of bytes in left available. See also: UltraMemAlloc, UltraMemFree, UltraMaxAlloc for C users UltraMemInit and for PASCAL users UltraMaxAvail UltraMemAlloc _________________________________ Purpose: To safely allocate a chunk of UltraSound DRAM. C: int UltraMemAlloc(size, location); unsigned long size; unsigned long *location; PASCAL: FUNCTION UltraMemAlloc( Size : LONGINT; VAR Location : LONGINT) : BOOLEAN; Remarks: This routine allocates a chunk of DRAM of 'Size' bytes from the UltraSound's DRAM. The memory allocation structures are set up by UltraOpen. 'Location' is filled in with the DRAM location of the start of the chunk of memory. The memory address returned will ALWAYS be aligned on a 32 byte boundary so that the DRAM can be DMA'ed into without error. Also, the size will be rounded UP to the next 32 byte boundary. PASCAL users can reference section 1.8.3 for a detailed look at how the memory is managed. Returns: C: ULTRA_OK if no problem. NO_MEMORY if there is no chunk of DRAM large enough. PASCAL: TRUE if the allocation was successful. If FALSE, check UltraErrorStr for the reason. See also: UltraMemFree, UltraMaxAlloc, for C users UltraMemInit and for PASCAL users UltraMaxAvail and UltraMemAvail ___________________________________________________________________________ 56 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraMemFree _________________________________ Purpose: To free a chunk of DRAM previously allocated with UltraMemAlloc. C: int UltraMemFree(size,location); unsigned long size; unsigned long location; PASCAL: FUNCTION UltraMemFree(Size : LONGINT; Location : LONGINT) : BOOLEAN; Remarks: Frees a previously allocated chunk of UltraSound memory. The size will automatically be rounded UP to the next 32 byte boundary. Returns: C: ULTRA_OK if no problem. CORRUPT_MEM if the memory structures are corrupted. PASCAL: TRUE if deallocation was successful. If FALSE, check UltraErrorStr for the reason. See also: UltraMemInit, UltraMemAlloc for C users UltraMemInit and for PASCAL users UltraMaxAvail and UltraMemAvail UltraMemInit _________________________________ Purpose: To initialize or re-initialize the memory pool structures. C: unsigned long UltraMemInit(void); PASCAL: This routine is not used. The unit automatically calls these routines when your program starts. Remarks: This routine sets up the UltraSound DRAM so that UltraMemAlloc and UltraMemFree will work. It is called in UltraOpen. It can be called at any time if an application wants to clean up all its allocated or corrupted memory structures. Returns: Number of K of DRAM found on the UltraSound. If an application wishes to reserve a chunk of DRAM outside of the memory pool, a variable called _ultra_reserved_dram must be set up with the # of bytes to reserve BEFORE UltraMemInit is called. This reserved chunk will start at 0. The reserved chunk must be greater than 32 bytes and less than 256K in size. See also: UltraMemAlloc, UltraMemFree, and UltraMaxAlloc ___________________________________________________________________________ 57 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraMIDIDisableRecv _________________________________ Purpose: To disable the MIDI receive data interrupt. C: void UltraMIDIDisableRecv(void); PASCAL: PROCEDURE UltraMIDIDisableRecv; Remarks: This routine will disable the receive data interrupts from the MIDI. If the interrupt is enabled, it should be disabled before leaving your application. See also: UltraMIDIEnableRecv, UltraMIDIRecvHandler UltraDisableMIDIXmit _________________________________ Purpose: To disable MIDI transmit interrupts. C: void UltraDisableMIDIXmit(void); PASCAL: PROCEDURE UltraDisableMIDIXmit; Remarks: This routine will turn off MIDI transmit interrupts. It MUST be called when you are through sending data. See also: UltraMIDIXmitHandler, UltraMIDIEnableXmit UltraMIDIEnableRecv _________________________________ Purpose: To enable receive data interrupts for MIDI port. C: void UltraMIDIEnableRecv(void); PASCAL: PROCEDURE UltraMIDIEnableRecv; Remarks: This routine will enable receive data interrupts from the MIDI port. It is necessary to set up a callback routine for your application to process the data. See also: UltraMIDIRecvhandler, UltraMIDIDisableRecv ___________________________________________________________________________ 58 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraEnableMIDIXmit _________________________________ Purpose: To enable transmit interrupts from MIDI. C: void UltraEnableMIDIXmit(void); PASCAL: PROCEDURE UltraEnableMIDIXmit; Remarks: This routine will enable transmit data interrupts to be generated as each byte is transmitted out the MIDI port. Note that a transmit interrupt will be generated as soon as the IRQ is enabled unless a byte is sent out immediately prior to enabling it. This is because the xmit buffer is initially empty (unless primed) so it will pop an interrupt. Also note that you MUST disable this interrupt when you are not sending any more data or else you will be hung up getting transmit ready interrupts. See also: UltraMIDIXmitHandler, UltraMIDIDisableXmit UltraMIDIRecv _________________________________ Purpose: To read a byte from the MIDI port. C: unsigned char UltraMIDIRecv(void); PASCAL: FUNCTION UltraMIDIRecv : BYTE; Remarks: This routine is used to read a byte from the MIDI port. It assumes that the byte is waiting. The Byte is there if it got to the MIDI receive interrupt callback routine or if you have polled the status and determined the receive buffer is full. Returns: MIDI data byte See also: UltraMIDIRecvHandler, UltraMIDIStatus ___________________________________________________________________________ 59 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraMIDIReset _________________________________ Purpose: To reset the MIDI port. C: void UltraMIDIReset(void); PASCAL: PROCEDURE UltraMIDIReset; Remarks: This routine should be used to ensure that the MIDI port is ready to use. All MIDI interrupts will be disabled by this call. See also: UltraEnableMIDIXmit, UltraEnableMIDIRecv UltraMIDIStatus _________________________________ Purpose: To read the MIDI status byte. C: unsigned char UltraMIDIStatus(void); PASCAL: FUNCTION UltraMIDIStatus : BYTE; Remarks: This routine returns the current MIDI port status bits. This can be used to determine if an error has occurred or if the port is ready to be read or written. Returns: The MIDI status byte. See chapter 2. See also: UltraMIDIXmit, UltraMIDIRecv UltraMIDIXmit _________________________________ Purpose: To send a byte out the MIDI port. C: void UltraMIDIXmit(data); unsigned char data; PASCAL: PROCEDURE UltraMIDIXmit(Data : BYTE); Remarks: This routine will send the 'Data' byte out the MIDI data port. If interrupts are enabled, an interrupt will be generated when the Byte has been transmitted. See also: UltraMIDIXmitHandler ___________________________________________________________________________ 60 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraOpen _________________________________ Purpose: To open and initialize the UltraSound card and the SDK code. C: int UltraOpen(config,voices); ULTRA_CFG *config; int voices; PASCAL: FUNCTION UltraOpen(VAR Config : Ultra_CFG; Voices : INTEGER) : BOOLEAN; Remarks: This routine should ALWAYS be called to initialize the UltraSound. It will probe for the card and program the IRQ and DMA latches. It will then disable line and microphone input and enable output. It also initializes the memory structures. The # of active voices is an important parameter to the UltraSound: the fewer the # of voices, the more oversampling that occurs on playback. This will make the sound much 'cleaner'. If you specify a number of voices less than 14, the card will still be initialized to use 14 voices. Likewise, if you select larger than 32 voices, you will still only have 32. Returns: C: ULTRA_OK if no problem. NO_ULTRA if no UltraSound card found. BAD_NUM_OF_VOICES if # of active voices out of range PASCAL: TRUE if the card was successfully opened. If FALSE, check UltraErrorStr for the reason. See also: UltraClose, UltraProbe ___________________________________________________________________________ 61 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraPeekData _________________________________ Purpose: To examine any DRAM location on the UltraSound. C: unsigned char UltraPeekData(baseport,location); unsigned int baseport; unsigned long location; PASCAL: FUNCTION UltraPeekData(PPort : INTEGER; Address : LONGINT) : BYTE; Remarks: This routine is used to allow an application to look at any location in UltraSound's DRAM. This can be handy for obtaining VU information or any other time it is nice to know what is in DRAM. Be aware that if the data is playable it will be in twos compliment form. If the data that you want is 16 bit data, you will need to peek both locations and do any appropriate conversions. The data will be in low/high format. That means that the low byte of the data will be in the even byte and the high byte will be in the odd byte. Returns: Data byte at location specified. See also: UltraPokeData, UltraUpload UltraPing _________________________________ Purpose: To quickly check to see if an UltraSound is present at a specified port. C: int UltraPing(baseport); int baseport; PASCAL: FUNCTION UltraPing(PPort : WORD) : BOOLEAN; Remarks: This routine will determine if an UltraSound is present by attempting to read and write to its DRAM. This routine assumes that at least a simple reset has been done since power-up so that the board is no longer in a reset state. If it is on a reset state, this routine will ALWAYS fail. UltraProbe pulls a quick reset and then calls UltraPing. Returns: C: ULTRA_OK if no problem. NO_ULTRA if no board is found at specified I/O port. PASCAL: TRUE if the card was found. FALSE otherwise. If unsuccessful, check UltraErrorStr for the reason. See also: UltraProbe ___________________________________________________________________________ 62 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraPokeData _________________________________ Purpose: To poke a byte into the UltraSound's DRAM. C: void UltraPokeData(baseport,location,data); unsigned int baseport; unsigned long location; unsigned char data; PASCAL: PROCEDURE UltraPokeData(PPort : INTEGER; Address : LONGINT; Data : BYTE); Remarks: Poke an 8 bit value directly into UltraSound DRAM. This can be useful to set the value of the location that a voice is pointing to. It is often desirable to point a voice to a known value since its output is ALWAYS summed in to the output even if the voice is not running. Be aware that there is no automatic conversion of data poked into DRAM. Since the UltraSound can only play twos compliment data, make sure that the data you are poking is in that format. Also be careful with 16 bit data. See also: UltraPeekData, UltraDownload ___________________________________________________________________________ 63 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraPrimeRecord _________________________________ Purpose: To prime the record DMA channel for use. C: int UltraPrimeRecord(pc_ptr,size,repeat); void far *pc_ptr; unsigned int size; int repeat; PASCAL: FUNCTION UltraPrimeRecord(PC_Ptr : POINTER; Size : WORD; RRepeat : BOOLEAN) : BOOLEAN; Remarks: This routine will setup the DMA channel to do a record, but does not start it. This can be used to help synchronize events. Programming the DMA channel can take enough time so that a few samples may be lost. (Depending on sample rate). This routine will help alleviate this problem by doing the programming ahead of time. Returns: C: ULTRA_OK if no problem. DMA_BUSY if DMA Channel is busy. PASCAL: TRUE if setup was successful. FALSE otherwise. If unsuccessful, check UltraErrorStr for the reason. See also: UltraGoRecord ___________________________________________________________________________ 64 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraPrimeVoice _________________________________ Purpose: To prime a voice with values but NOT start it. C: unsigned char UltraPrimeVoice(voice,begin,start,end,mode); int voice; unsigned long begin; unsigned long start; unsigned long end; unsigned char mode; PASCAL: FUNCTION UltraPrimeVoice(Voice : INTEGER; VBegin : LONGINT; VStart : LONGINT; VEnd : LONGINT; VMode : BYTE) : BYTE; Remarks: This routine is used to do all the setup necessary to start a voice but does NOT start it up. This can be useful if you want to start more than 1 voice at the same time. Use UltraPrimeVoice to do all the necessary setup and then use UltraGoVoice to start the voices. UltraStartVoice calls UltraPrimeVoice and then immediately UltraGoVoice. Returns: An updated mode value. The mode may be modified on the basis of the location parameters. This altered value should be the one passed to UltraGoVoice. See also: UltraStartVoice, UltraGoVoice ___________________________________________________________________________ 65 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraProbe _________________________________ Purpose: To probe for the existence of an UltraSound at a specified port. C: int UltraProbe(base_port); unsigned int base_port; PASCAL: FUNCTION UltraProbe(PPort : WORD) : BOOLEAN; Remarks: This routine probes for the existence of an UltraSound card at the specified base port. An application could call this before calling UltraOpen to see if a card is present. UltraOpen calls this routine also. The difference between UltraProbe and UltraPing is that UltraProbe will pull a reset to make sure the board is running. UltraPing assumes this has already been done. Returns: C: ULTRA_OK if no problem. NO_ULTRA if no card was found at the port specified. PASCAL: TRUE if the card is found. FALSE otherwise. If unsuccessful, check UltraErrorStr for the reason. See also: UltraOpen, UltraPing ___________________________________________________________________________ 66 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraRampVolume _________________________________ Purpose: To ramp a voice's volume up or down. C: void UltraRampVolume(voice,start,end,rate,mode); int voice; unsigned int start; unsigned int end; unsigned char rate; unsigned char mode; PASCAL: PROCEDURE UltraRampVolume(Voice : INTEGER; StartV : WORD; EndV : WORD; VRate : BYTE; VMode : BYTE); Remarks: Start a volume ramp from a starting volume to the ending volume. The rate at which the ramp occurs can be calculated using UltraCalcRate. The mode determines the looping and interrupting characteristics of the ramp. If you choose to have it interrupt at the end of the ramp, you should set up an routine to call for a volume interrupt. This is done with the routine UltraVolumeHandler. The volume mode bits are defined in Appendix B. See also: UltraCalcRate, UltraVolumeHandler UltraReadRecordPosition _________________________________ Purpose: To return the # of bytes recorded so far in a record. C: unsigned int UltraReadRecordPosition(void); PASCAL: FUNCTION UltraReadRecordPosition : WORD; Remarks: This routine can be used to monitor the amount data recorded. Returns: Number of BYTES recorded so far. See also: UltraRecordData ___________________________________________________________________________ 67 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraReadVoice _________________________________ Purpose: To read a voice's current location. C: unsigned long UltraReadVoice(voice); int voice; PASCAL: FUNCTION UltraReadVoice(Voice : INTEGER) : LONGINT; Remarks: This routine can be used to monitor a voice's progress. Returns: The voice's current position in DRAM. See also: UltraSetVoice UltraReadVolume _________________________________ Purpose: To read a voice's current volume. C: unsigned int UltraReadVolume(voice); int voice; PASCAL: FUNCTION UltraReadVolume(Voice : INTEGER) : WORD; Remarks: This routine returns the current volume of a voice. This can be useful when used in conjunction with volume ramps. The value returned is logarithmic, not linear. PASCAL users can use UltraReadLinearVolume. Returns: 0-4095 value, the current logarithmic volume. See also: UltraSetVolume, UltraRampVolume ___________________________________________________________________________ 68 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraRecordData _________________________________ Purpose: To record some data with the UltraSound from the active 'In' ports. C: int UltraRecordData(pc_ptr,control,size,wait,repeat); void far *pc_ptr; unsigned char control; unsigned int size; int wait; int repeat; PASCAL: FUNCTION UltraRecordData(PC_Ptr : POINTER; Control : BYTE; Size : WORD; Wait : BOOLEAN; RRepeat : BOOLEAN) : BOOLEAN; Remarks: This routine will record a buffer of data from UltraSound. It can be in either 8-bit mono or stereo. In stereo, there are two bytes and the left byte is first. If mono is being used, the left channel is the one that is sampled. See Appendix E for the a description of the recording control bits. If 'wait' is set to a non-zero value, then this routine will not return until the buffer has been filled. If 'repeat' is TRUE, then the DMA channel will be set up in auto-init mode so that the recording is done indefinitely. If this is done, then the buffer MUST reside completely in 1 64K page of PC RAM. Also, it is probably necessary that your application hooks to the record handler so that the control register on the UltraSound can be hit to restart the recording process (UltraGoRecord). This will be very quick since the PC DMA controller will not be re-programmed. Returns: C: ULTRA_OK if no problem. DMA_BUSY if sampling DMA Channel is busy. BAD_DMA_ADDR if autoinit buffer crosses 64k page. PASCAL: TRUE if successful. FALSE otherwise. If unsuccessful, check UltraErrorStr for the reason. See also: UltraWaitRecordDMA, UltraSetRecordFrequency UltraGoRecord ___________________________________________________________________________ 69 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraRecordDMABusy _________________________________ Purpose: To see if the record DMA channel is busy. C: int UltraRecordDMABusy(void); PASCAL: FUNCTION UltraRecordDMABusy : BOOLEAN; Remarks: This routine checks to see if the record DMA channel is busy. It might be busy doing a record (or playback if the record & playback channels are the same). Returns: C: 0 = Channel not Busy 2 = Channel Busy PASCAL: TRUE if the channel is not available. FALSE if it is. See also: UltraRecordData UltraReset _________________________________ Purpose: To reset the UltraSound. C: int UltraReset(voices); int voices; PASCAL: FUNCTION UltraReset(Voices : INTEGER) : BOOLEAN; Remarks: This routine is called by UltraOpen to make sure the card is in a known state. UltraClose also calls this routine. Returns: C: ULTRA_OK if no problem. BAD_NUM_OF_VOICES if # of voices not in range. PASCAL: TRUE if card is successfully reset. If FALSE, check UltraErrorStr for the reason. See also: UltraOpen, UltraClose ___________________________________________________________________________ 70 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraSetBalance _________________________________ Purpose: To set a voice's pan position. C: void UltraSetBalance(voice,data); int voice; int data; PASCAL: PROCEDURE UltraSetBalance(Voice : INTEGER; Data : BYTE); Remarks: This routine sets the voice's position between right and left speakers. A 0 will place the audio all the way to the left whereas a 15 will put the sound all the way to the right. Values that are out of range will move the balance to the nearest extreme. UltraSetFrequency _________________________________ Purpose: To set a voice's playback frequency. C: void UltraSetFrequency(voice,speed_hz); int voice; unsigned long speed_hz; PASCAL: PROCEDURE UltraSetFrequency(Voice : INTEGER; Speed_Khz : LONGINT); Remarks: This routine sets the voice's playback rate to the specified absolute frequency. The number of active voices is taken into account when making the appropriate calculations. UltraSetLoopMode _________________________________ Purpose: To set a voice's loop mode. C: void UltraSetLoopMode(voice,mode); int voice; unsigned char mode; PASCAL: PROCEDURE UltraSetLoopMode(Voice : INTEGER; VMode : BYTE); Remarks: This routine will set this voice's looping mode to the specified mode. See Appendix C for the definition of these bits. ___________________________________________________________________________ 71 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraSetRecordFrequency _________________________________ Purpose: To set the recording rate. C: void UltraSetRecordFrequency(rate); unsigned long rate; PASCAL: PROCEDURE UltraSetRecordFrequency(Rate : LONGINT); Remarks: This routine sets the record rate to the specified frequency. Since the UltraSound uses the PC DMA channel to do the sampling directly into PC RAM, no voice specification is necessary. UltraSetVoice _________________________________ Purpose: To set a voice to an absolute position in DRAM. C: void UltraSetVoice(voice,location); int voice; unsigned long location; PASCAL: PROCEDURE UltraSetVoice(Voice : INTEGER; Location : LONGINT); Remarks: This routine sets a voice's current position to an absolute location. This can be useful to set a voice to a location with a known value since all voices' current locations are summed in to the output even if the voice is not running. 'Pops' in the audio may result if a voice is set to a location that contains a significant value. UltraSetVoiceEnd _________________________________ Purpose: To set a voice's end position. C: void UltraSetVoiceEnd(voice,end); int voice; unsigned long end; PASCAL: PROCEDURE UltraSetVoiceEnd(Voice : INTEGER; VEnd : LONGINT); Remarks: This routine sets a new endpoint for the specified voice. Used in conjunction with UltraSetLoopMode to turn looping off, a sampled decay can be implemented. See also: UltraSetLoopMode ___________________________________________________________________________ 72 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraSetVolume _________________________________ Purpose: To set a voice's current logarithmic volume. C: void UltraSetVolume(voice,volume); int voice; unsigned int volume; PASCAL: PROCEDURE UltraSetVolume(Voice : INTEGER; Volume : WORD); Remarks: This routine sets the volume of the voice to a specific logarithmic value. The range is from 0 to 4095. Use UltraSetLinearVolume to do linear volumes. See also: UltraSetLinearVolume UltraSizeDRAM _________________________________ Purpose: To Find the amount of DRAM available on the UltraSound. C: int UltraSizeDram(void); PASCAL: FUNCTION UltraSizeDRAM : INTEGER; Note: This routine will always return the same value, which is the amount of DRAM that has been installed on the card. Use UltraMemAvail to see how much DRAM is still free. Remarks: For C users, this routine could be used by an application to determine how much it can load into the UltraSound. UltraMemInit calls this to determine how much can be used for its memory pool. Returns: # of kilobytes found on the UltraSound. See also: UltraMemInit ___________________________________________________________________________ 73 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraStartTimer _________________________________ Purpose: To set up and start either Timer 1 or Timer 2. C: void UltraStartTimer(timer,duration); int timer; unsigned char duration; PASCAL: PROCEDURE UltraStartTimer(Timer : INTEGER; Duration : BYTE); Remarks: This routine can be used to start up one of two available hardware timers. Timer #1 is an 80 microsecond (.00008s) timer. Timer #2 is a 320 microsecond (.00032s) timer. When starting either timer, you supply the # of counts before the timer 'ticks'. When the timer 'ticks', it ALWAYS calls the callback routine defined for it. If you don't supply one, a default is used. These timers can be used to trigger various real time events. They are used extensively in music compostition programs. Please remember that the callback routine is called directly from the interrupt handler, so you must be careful what you do in the callback routine. Note that the specified timer is automatically restarted after it ticks. Your application must explicitly call UltraStopTimer to shut it off. See also: UltraStopTimer, UltraTimerStopped UltraTimer1Handler, UltraTimer2Handler. Example: Giving Timer 1 a duration of 10 counts would make the timer tick every 800 microseconds (.0008s). This would mean your callback routine is called 1250 times a second. ___________________________________________________________________________ 74 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraStartVoice _________________________________ Purpose: To set up and start a voice playing. C: void UltraStartVoice(voice,begin,start,end,mode); int voice; unsigned long begin; unsigned long start; unsigned long end; unsigned char mode; PASCAL: PROCEDURE UltraStartVoice(Voice : INTEGER; VBegin : LONGINT; VStart : LONGINT; VEnd : LONGINT; VMode : BYTE); Remarks: This routine will set up and start up a voice. The voice will begin playback at the 'begin' and continue to 'end'. If looping is enabled for this voice, then it will then jump to 'start' and then continue to 'end'. The method of looping is determined by 'mode'. See Appendix C for the definition of these bits. See also: UltraSetVoiceEnd, UltraSetLoopMode UltraStopTimer _________________________________ Purpose: To stop a timer which has been previously activated. C: void UltraStopTimer(timer); int timer; PASCAL: PROCEDURE UltraStopTimer(Timer : INTEGER); Remarks: If you have started a timer with UltraStartTimer, you will probably want to shut it off before shutting down your application. If you forget, UltraClose automatically shuts it down. See also: UltraStartTimer ___________________________________________________________________________ 75 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraStopVoice _________________________________ Purpose: To stop a voice which is playing. C: void UltraStopVoice(voice); int voice; PASCAL: PROCEDURE UltraStopVoice(Voice : INTEGER); Remarks: This routine will stop a voice which is playing at it's current position. Don't forget that if a voice is left at an unknown position, the data that is at that position will still be summed into the final output. See also: UltraStartVoice, UltraSetVoiceEnd UltraStopVolume _________________________________ Purpose: To stop a currently active volume ramp. C: void UltraStopVolume(voice); int voice; PASCAL: PROCEDURE UltraStopVolume(Voice : INTEGER); Remarks: You can use this routine if you wish to stop an volume ramp after a specified period of time, or you wish to make sure that a volume ramp has stopped. See also: UltraRampVolume, UltraRampLinearVolume UltraTimerStopped _________________________________ Purpose: To check to see if a timer is running. C: int UltraTimerStopped(timer); int timer; PASCAL: FUNCTION UltraTimerStopped(Timer : INTEGER) : BOOLEAN; Remarks: This routine can be used to see if a timer is currently being used. Returns: FALSE if still running, TRUE if stopped. See also: UltraStartTimer, UltraStopTimer ___________________________________________________________________________ 76 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraTrimJoystick _________________________________ Purpose: To set the trim voltage on the joystick port. C: void UltraTrimJoystick(value); unsigned char value; PASCAL: PROCEDURE UltraTrimJoystick(JoyVal : BYTE); Remarks: This routine is used to set the speed compensation value on the joystick port on the UltraSound. The faster the computer, the smaller this value should be. This allows all software that reads the joystick to return consistent joystick positions regardless of the speed of the machine. This is normally not needed and probably should never be used in your application. The utility ULTRAJOY.EXE which is included with all UltraSound stock software is used to set this value. UltraUpload _________________________________ Purpose: To transfer the contents of a block of DRAM to PC RAM. C: int UltraUpload(dataptr,control,dram_loc,len,wait); void *dataptr; unsigned char control; unsigned long dram_loc; unsigned int len; int wait; PASCAL: FUNCTION UltraUpload(DataPtr : POINTER; Control : BYTE; DRAM_Loc : LONGINT; Len : WORD; Wait : BOOLEAN) : BOOLEAN; Remarks: This routine will retrieve a chunk of data from the UltraSound's DRAM. It will transfer 'len' # of bytes to DataPtr (in PC) from DRAM_loc (in UltraSound). If 'Wait' is TRUE, then it will wait until the transfer is complete. If 'Wait' is FALSE, it will return as soon as transfer is started. See Appendix D for a definition of the control bits. They specify the type of data being retrieved. Returns: C: ULTRA_OK if no problem. DMA_BUSY if DMA Channel is busy. PASCAL: TRUE if transfer was successful. FALSE otherwise. If unsuccessful, check UltraErrorStr for the reason. See also: UltraDownload, UltraDRAMDMAWait ___________________________________________________________________________ 77 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraVectorVolume _________________________________ Purpose: To ramp a volume from it's current position to a new value. C: void UltraVectorVolume(voice,end_idx,rate,mode); int voice; unsigned int end; unsigned char rate; unsigned char mode; PASCAL: PROCEDURE UltraVectorVolume(Voice : INTEGER; VEnd : WORD; VRate : BYTE; VMode : BYTE); Remarks: This routine can be used to ramp from an unknown volume value to a new value. It is useful if you are doing volume envelopes and need to restart the attack/decay sequence at any time. See also: UltraRampVolume, UltraStopVolume UltraVersion _________________________________ Purpose: To return the version of the SDK code being used. C: void UltraVersion(major,minor); int *major; int *minor; PASCAL: PROCEDURE UltraVersion(VAR Major : BYTE; VAR Minor : BYTE); Remarks: This routine can be useful to track the version of the SDK that was used in compiling your source code. Returns: Major (1 digit) and minor (2 digits) version of the unit. See also: UltraVersionStr ___________________________________________________________________________ 78 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraVersionStr _________________________________ Purpose: To return the version of the SDK code being used in a string. C: This function is not available. PASCAL: FUNCTION UltraVersionStr : String; Remarks: This routine can be useful to track the version of the SDK that was used in compiling your source code. Returns: Major and minor version of the unit, in string format. See also: UltraVersion UltraVoiceStopped _________________________________ Purpose: To return whether a voice is playing. C: int UltraVoiceStopped(voice); int voice; PASCAL: FUNCTION UltraVoiceStopped(Voice : INTEGER) : BOOLEAN; Remarks: This routine can be used to see if a sample has finished playing . Returns: TRUE if the voice is stopped, FALSE otherwise. UltraVolumeStopped _________________________________ Purpose: To determine if a volume ramp is running for a particular voice. C: int UltraVolumeStopped(voice); int voice; PASCAL: FUNCTION UltraVolumeStopped(Voice : INTEGER) : BOOLEAN; Remarks: This routine is used to determine the current state of the volume of the voice. It can be used to see if a volume ramp is still running. Returns: TRUE if no volume ramp is running, FALSE otherwise. See also: UltraRampVolume, UltraRampLinearVolume ___________________________________________________________________________ 79 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraWaitDRAMDMA _________________________________ Purpose: To wait for a DRAM DMA transfer to complete. C: void UltraWaitDramDMA(void); PASCAL: PROCEDURE UltraWaitDRAMDMA; Remarks: If a DMA transfer to/from DRAM is started but told not to wait for it to complete, this routine can be used to wait until it has completed. See also: UltraDownload, UltraUpload UltraWaitRecordDMA _________________________________ Purpose: To wait for a recording DMA transfer to complete. C: void UltraWaitRecordDMA(void); PASCAL: PROCEDURE UltraWaitRecordDMA; Remarks: This can be used to let an application wait until a complete sample is finished being acquired. See also: UltraRecordData ___________________________________________________________________________ 80 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraAllocVoice _________________________________ Purpose: To allocate a new voice not currently being used. C: int UltraAllocVoice(voice_num,new_num); int voice_num; int *new_num; PASCAL: FUNCTION UltraAllocVoice(VAR Voice_Num : INTEGER) : BOOLEAN; Remarks: This routine will return a voice for your application to use. If you supply a voice number, it will attempt to allocate that particular voice. If you pass a -1 for the voice number, it will return the next free voice. This routine will only allocate voices up to the # of active voices specified in the UltraOpen function. Returns: C: ULTRA_OK Got a voice ok VOICE_NOT_FREE Can't get the voice or none left. VOICE_OUT_OF_RANGE Specified voice out of range. PASCAL: TRUE if there was no problem allocating the voice. If FALSE, check UltraErrorStr for the reason. See also: UltraClearVoices, UltraFreeVoices UltraClearVoices _________________________________ Purpose: To reset all voices to an un-allocated state. C: void UltraClearVoices(void); PASCAL: PROCEDURE UltraClearVoices; Remarks: This routine will deallocate all previously allocated voices. It would be advisable to call this before using either UltraAllocVoice or UltraFreeVoice. See also: UltraAllocVoice, UltraFreeVoice. ___________________________________________________________________________ 81 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraFreeVoice _________________________________ Purpose: To free up an allocated voice. C: void UltraFreeVoice(voice_num); int voice_num; PASCAL: PROCEDURE UltraFreeVoice(Voice_Num : INTEGER); Remarks: This routine will free up a previously allocated voice. This should be used when your application no longer needs the voice so it can be re-allocated at another time. See also: UltraClearVoices, UltraAllocVoice. UltraVoiceOff _________________________________ Purpose: To allow flexibility in stopping a voice. C: void UltraVoiceOff(voice,end); int voice; int end; PASCAL: PROCEDURE UltraVoiceOff(Voice : INTEGER; VEnd : BOOLEAN); Remarks: This routine will either stop a voice immediately or let it finish its current loop. If 'VEnd' is FALSE then it will stop abruptly, otherwise it will finish the current loop. UltraReadVoice could be called afterwards if you need to know where the loop finished. If used with UltraSetVoiceEnd, you could implement a sampled decay on the end of your sample. This would occur if your loop point was not at the end of your data, and you changed the end point to the real end point of your data and then called this routine with VEnd set to TRUE. See also: UltraReadVoice, UltraSetVoiceEnd ___________________________________________________________________________ 82 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraVoiceOn _________________________________ Purpose: To turn a voice on at a given frequency. C: void UltraVoiceOn(voice,begin,s_loop,e_loop,control,freq); int voice; unsigned long begin; unsigned long s_loop; unsigned long e_loop; unsigned char control; unsigned long freq; PASCAL: PROCEDURE UltraVoiceOn(Voice : INTEGER; VBegin : LONGINT; Start_Loop : LONGINT; End_Loop : LONGINT; Control : BYTE; Freq : LONGINT); Remarks: This routine just sets the frequency with UltraSetFrequency and then calls UltraStartVoice. See also: UltraSetFrequency, UltraStartVoice UltraSetLinearVolume _________________________________ Purpose: To set a voice's volume to a linearized value. C: void UltraSetLinearVolume(voice,index); int voice; int index; PASCAL: PROCEDURE UltraSetLinearVolume(Voice : INTEGER; Index : INTEGER); Remarks: This routine indexes into a table to translate a linear volume (0-511) to a logarithmic one (0-4095), and then calls UltraSetVolume. See also: UltraSetVolume ___________________________________________________________________________ 83 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraReadLinearVolume _________________________________ Purpose: To read a voice's volume as a linear value. C: This function is not available. PASCAL: FUNCTION UltraReadLinearVolume(Voice : INTEGER) : INTEGER; Remarks: This routine indexes into a table to translate a 0-4095 logarithmic volume to a 0-511 linear volume. Returns: Linear Volume Value (0-511). See also: UltraSetVolume, UltraSetLinearVolume UltraRampLinearVolume _________________________________ Purpose: To ramp a voice's volume between linear volume values. C: void UltraRampLinearVolume(voice,start,end,msecs,mode); int voice; unsigned int start; unsigned int end; unsigned long msecs; unsigned char mode; PASCAL: PROCEDURE UltraRampLinearVolume(Voice : INTEGER; Start_Idx : WORD; End_Idx : WORD; Msecs : LONGINT; VMode : BYTE); Remarks: This routine is used to ramp between linear volume settings. It uses the same method as UltraSetLinearVolume to determine the actual volume settings to use, and then calls UltraRampVolume. See also: UltraRampVolume, UltraSetLinearVolume ___________________________________________________________________________ 84 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraVectorLinearVolume _________________________________ Purpose: To ramp a linear volume from it's current position to a known end point. C: void UltraVectorLinearVolume(voice,end,rate,mode) int voice; unsigned int end; unsigned char rate; unsigned char mode; PASCAL: PROCEDURE UltraVectorLinearVolume(Voice : INTEGER; End_Idx : WORD; VRate : BYTE; VMode : BYTE); Remarks: This routine can be used to ramp from an unknown volume to a new linear volume. It is useful if you are doing volume envelopes and need to restart the attack/decay sequence at any time. It can also produce a smoother ramp from one volume to another, since arbitrarily setting a volume that is far away from the current volume can cause 'pops'. See also: UltraSetLinearVolume, UltraReadLinearVolume ___________________________________________________________________________ 85 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Chapter 5 - Focal Point 3D Sound 5.0 Introduction Three dimensional audio on the UltraSound is achieved by a technique called binaural representation. Basically, this means that a mono sound is 'shaped' in such a way that when it is presented to the right and left ears properly, the sound seems to come from the proper place in space. This technique is also called convolution. This is done thru algorithms developed by Focal Point(tm) 3D Audio. Focal Point has provided a utility (called FP3D.EXE) to convert a mono sound file to a 3D file capable of being played on an UltraSound. The basic concept is that the mono sound is processed in such a way that the file that is output contains up to 6 tracks of sound. When the sound is played back, the volumes and balances are adjusted to make the sound appear to originate from anywhere in the 3D space. It is possible to create files that only have 4 tracks and can therefore only be positioned in 2 dimensions. This may be adequate for many applications and it will make the resultant file smaller. There are 2 types of 3D sound that an application might want to use. The first is a sound effect. This is a sound that can be completely loaded into DRAM and played and positioned at any time. It is used for things like gunshots, cars etc. These can be looped or non-looped. The other type of sound is a sound track. This is for a very long sound that cannot be loaded in DRAM all at once. The implementation of each of these methods is very different. A sound effect is implemented using a blocked data format. This means that each track's data is in a block of its own. A sound track uses interleaved data. This means that all the track's data is interleaved together. For example, a blocked file would look like this: HEADER FFFFFRRRRRBBBBBLLLLLUUUUUDDDDD.... whereas an interleaved file would look like this: HEADER FRBLUDFRBLUDFRBLUDFRBLUDFRBLUD... where F = Front Track L = Left Track R = Right Track U = Up Track B = Behind Track D = Down Track Both of these methods have their individual advantages and disadvantages. First, the interleaved method makes it very easy to read in the data in a continuous stream since it will look the same all the way through the file. It is also faster to read 60K of data once rather than 6 10K reads. This makes it very useful for a sound track. However, one drawback is that it has a very limited number of frequencies that it will run at. The reason for this is rather difficult to explain, but it pertains to getting the UltraSound's voices to play every 4th (or 5th, etc) sample. The UltraSound normally would interpolate between data points but it can't do that here because the adjacent data points are not in the same track. ___________________________________________________________________________ 86 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ If you want to run at 22050 Hz, then you need to have only 28 active voices to accomplish this. If you want to play a track at 44100 Hz, you must have only 14 active voices. Blocked data doesn't have this problem. Since the data is NOT interleaved, but in a block of contiguous DRAM, its frequency can be adjusted to any value. This is usually very useful for sound effects (rev'ing engines etc). Also, since the data is blocked, each track's data can be allocated separately and can therefore be up to 256K. An interleaved file must fit into 1 256K bank since we cannot play 16bit data across a 256K bank. See the two example 3D programs provided (PLAY3D and PLAY3DI) for ideas on how to implement 3D sound into you applications. 5.1 Creating 3D file Please read the readme.3D file for information on creating a 3D file. 5.2 3D Sound Routines UltraAbsPosition _________________________________ Purpose: To place a sound using integer Cartesian coordinates. C: void UltraAbsPosition3D(sound,xpos,ypos,zpos); SOUND_3D *sound; int xpos; int ypos; int zpos; PASCAL: PROCEDURE UltraAbsPosition3D(VAR Sound : SOUND_3D; XPos : INTEGER; YPos : INTEGER; ZPos : INTEGER); Remarks: This routine will position a sound using standard cartesian coordinates. The X position range is from -511 (left) to +511 (right). The Y position is from -511 (below) to +511 (above). The Z position is from -511 (behind) to +511 (ahead). The X and Z positions determine the azimuth position and Y determines the elevation. The distance away from the listener is determined using trigonometry. Once these are determined, UltraAngPosition3D is called. If the distance is calculated to be greater than 511, it is clipped to 511. Also, if the distance is calculated to be 0, no positioning is done since the origin is undefined. (i.e. A sound cannot be generated INSIDE your head (origin)). C users should be careful when using this routine, since the trig functions will be needed from the C libraries. See also: UltraAngPosition3D ___________________________________________________________________________ 87 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraAngPosition3D _________________________________ Purpose: To position a sound using integer polar coordinates. C: void UltraAngPosition3D(sound,azimuth,elevation,volume); SOUND_3D *sound; int azimuth; int elevation; int volume; PASCAL: PROCEDURE UltraAngPosition3D(VAR Sound : SOUND_3D; AZIMuth : INTEGER; Elevation : INTEGER; Vol_Dist : INTEGER); Remarks: This routine will position a 3D sound using polar coordinates. The azimuth and elevation are specified in degrees. Azimuth is the angle in the horizontal plane. Straight ahead is 0 degrees, 90 degrees is to the right, -90 degrees is to the left and 180 (or -180) is directly behind you. If an angle larger than 180 or smaller that -180 is specified, it is converted to its -180 to 180 equivalent. For example, 270 degrees is equivalent to - 90 degrees. Elevation is the angle of elevation above or below the horizontal plane. 0 degrees is no elevation, 90 degrees is straight up, and -90 is straight down. Any angle larger than 90 or smaller that -90 is ignored. If more precision is needed, use UltraAngFltPosition3D so that floating point arithmetic is used. Since that routine uses floating point functions (slow), your application may not want to use it. See also: UltraAbsPosition3D ___________________________________________________________________________ 88 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraAngFltPosition3D _________________________________ Purpose: To position a sound using floating point polar coordinates. C: void UltraAngFltPosition3D(sound,azimuth,elevation,volume); SOUND_3D *sound; double azimuth; double elevation; int volume; PASCAL: PROCEDURE UltraAngFltPosition3D(VAR Sound : SOUND_3D; AZIMuth : DOUBLE; Elevation : DOUBLE; Vol_Dist : INTEGER); Remarks: This routine will position a 3D sound using polar coordinates. The azimuth and elevation are specified as floating point numbers in degrees. For example, 45 and one half degrees would be specified as 45.5. Azimuth is the angle in the horizontal plane. Straight ahead is 0 degrees, 90 degrees is to the right, -90 degrees is to the left and 180 (or -180) is directly behind you. If an angle larger than 180 or smaller that -180 is specified, it is converted to its -180 to 180 equivalent. For example, 270 degrees is equivalent to -90 degrees. Elevation is the angle above or below the horizontal plane. 0 degrees is no elevation, 90 degrees is straight up, and -90 is straight down. Any angle larger than 90 or smaller that -90 is ignored. If your application does not need to use the floating point routine, it is advisable to use UltraAngPosition because it avoids using some floating-point functions (which are slow). UltraCloseDup3D _________________________________ Purpose: To close a duplicated 3D sound effect. C: void UltraCloseDup3D(sound); SOUND_3D *sound; PASCAL: PROCEDURE UltraCloseDup3D(VAR Sound : SOUND_3D); Remarks: This routine must be used to free up the resources associated with a duplicated 3D effect. This routine will only release the voices used by the duplicated effect, not the DRAM allocated by the original effect. The original effect should be closed with UltraUnLoad3DEffect. See also: UltraDup3D, UltraUnLoad3DEffect. ___________________________________________________________________________ 89 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltrDup3D _________________________________ Purpose: To copy a 3D sound effect. C: int UltraDup3D(old,new); SOUND_3D *old; SOUND_3D *new; PASCAL: FUNCTION UltraDup3D(VAR Current : SOUND_3D; VAR Sound : SOUND_3D) : BOOLEAN; Remarks: This routine is very useful if you want to use the same sound effect in multiple places at once. This allows you to use the same DRAM data and just allocate some more voices to move the sound. This helps to save DRAM space. Make sure that you use UltraCloseDup3D to free the voices, NOT UltraUnLoad3DEffect. UltraUnLoad3DEffect will release all the DRAM for the sound. This new effect can be passed to any other routines the same way the original effect is. Returns: C: ULTRA_OK No Error NO_FREE_VOICES Not enough voices to make new handle PASCAL: TRUE if duplication successful. If FALSE, check UltraErrorStr for the reason. See also: UltraCloseDup3D ___________________________________________________________________________ 90 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraLoad3DEffect _________________________________ Purpose: To load a 3D effect from the disk into DRAM. C: int UltraLoad3DEffect(sound,filename,pc_buffer,size); SOUND_3D *sound; char *filename; void far *pc_buffer; unsigned int size; PASCAL: FUNCTION UltraLoad3DEffect(VAR Sound : SOUND_3D; VAR FileName : STRING; PC_Buffer : POINTER; Size : WORD) : BOOLEAN; Remarks: This routine is used to load a 3D sound into the DRAM on the UltraSound. It will allocate all necessary resources so that UltraStart3D can be called later. As many buffers of DRAM as needed will be allocated along with the proper number of voices. These resources will be freed up when you call UltraUnLoad3DEffect. The file specified MUST be a properly formatted or the routine will fail. This means that it must have the proper header and 3D data in it. The 'pc_buffer' is a buffer supplied by your application for this routine's use to download data into the UltraSound. The 'size' parameter is the size of the 'pc_buffer'. The larger this buffer is, the faster it will download. Returns: C: ULTRA_OK No error NO_3D_FILE 3D file not found BAD_3D_HDR 3D header is bad NO_3D_HDR File doesn't have a valid header NOT_BLOCK_DATA File is not in blocked format NO_FREE_VOICES Not enough free voices BAD_FILE_DATA Not enough data or bad file CORRUPT_MEM Memory structs have been corrupted DMA_BUSY DMA channel busy PASCAL: TRUE if the file could be loaded. If FALSE check UltraErrorStr for the reason. See also: UltraMemAlloc, UltraVoiceAlloc, UltraDownload ___________________________________________________________________________ 91 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraSetFreq3D _________________________________ Purpose: To set the frequency of a 3D sound effect. C: void UltraSetFreq3D(sound,frequency); SOUND_3D *sound; unsigned long frequency; PASCAL: FUNCTION UltraSetFreq3D(VAR Sound : SOUND_3D; Frequency : LONGINT) : BOOLEAN; Remarks: This routine will allow you to alter the frequency that the 3D sound is using. This allows you to do pitch shifting to get a doppler shift type effect. This routine can only be done on blocked data. Returns: C: ULTRA_OK No problem NOT_BLOCKED_DATA Can't change freq of interleaved data PASCAL: TRUE if the frequency was changed ok. If FALSE, check UltraErrorStr for the reason. UltraRelease3DInterleave _________________________________ Purpose: To release 3D resources for an interleaved effect. C: void UltraRelease3DInterleave(sound) SOUND_3D *sound; PASCAL: PROCEDURE UltraRelease3DInterleave(VAR Sound : SOUND_3D); Remarks: This routines will release the DRAM and voices allocated for an interleaved sound track. See also: UltraSetup3DInterleave ___________________________________________________________________________ 92 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraSetup3DInterleave _________________________________ Purpose: To set up for an interleaved 3D sound effect. C: int UltraSetup3DInterleave(sound,filename,size); SOUND_3D *sound; char *filename; unsigned long size; PASCAL: FUNCTION UltraSetup3DInterleave(VAR Sound : SOUND_3D; VAR FileName : STRING; Size : LONGINT) : BOOLEAN; Remarks: This routine will allocate the voices and memory necessary to play back an interleaved 3D sound. It will NOT load any of the file data into DRAM. Your application is responsible for that. It will open the file, read the header, allocate the appropriate resources, and set up the begin, start and end loop points for each voice. Since the sound is interleaved, the loop points are staggared appropriately. Returns: C: ULTRA_OK No error NO_3D_FILE 3D file not found BAD_3D_HDR 3D header is bad NO_3D_HDR File doesn't have a valid header NOT_INTERLEAVED_DATA File in blocked format NO_FREE_VOICES Not enough free voices BAD_FILE_DATA Not enough data or bad file CORRUPT_MEM Memory structs have been corrupted PASCAL: TRUE if the file could be read successfully. If FALSE, check UltraErrorStr for the reason. See also: UltraLoad3DEffect UltraStart3D _________________________________ Purpose: To start a 3D sound which has been loaded. C: void UltraStart3D(sound); SOUND_3D *sound; PASCAL: PROCEDURE UltraStart3D(VAR Sound : SOUND_3D); Remarks: This routine will start a 3D sound. If you want it to begin at a specific point in space, be sure that you position it first. It is not necessary to stop the sound before starting it again. See also: UltraStart3D ___________________________________________________________________________ 93 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ UltraStop3D _________________________________ Purpose: To stop a 3D sound which is playing. C: void UltraStop3D(sound,abruptly); SOUND_3D *sound; int abruptly; PASCAL: PROCEDURE UltraStop3D(VAR Sound : SOUND_3D; Abruptly : BOOLEAN); Remarks: This routine will stop a 3D sound. There are two ways to stop a sound: if the 'abruptly' flag is TRUE, then the sound will shut off immediately; if it is FALSE, then the sound will be ramped down very quickly. The second method will give a smoother transition. UltraUnLoad3DEffect _________________________________ Purpose: To free resources used by a 3D effect. C: void UltraUnLoad3DEffect(sound); SOUND_3D *sound; PASCAL: PROCEDURE UltraUnLoad3DEffect(VAR Sound : SOUND_3D); Remarks: This routine is used to free up all the resources (voices & DRAM) that a 3D sound uses. Since a 3D sound can use a lot of the voices & DRAM, you should free up the resources whenever you can. See also: UltraLoad3DEffect ___________________________________________________________________________ 94 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Appendix A - Error Codes ====== C: ====== Main routines: (these are defined in ULTRAERR.H) ULTRA_OK 1 No error BAD_NUM_OF_VOICES 2 must be 14-32 NO_MEMORY 3 Not enough free DRAM left CORRUPT_MEM 4 memory structures are corrupt NO_ULTRA 5 Can't find an UltraSound DMA_BUSY 6 This DMA channel is still busy BAD_DMA_ADDR 7 auto init across page boundaries VOICE_OUT_OF_RANGE 8 allocate a voice past # active VOICE_NOT_FREE 9 voice has already been allocated NO_FREE_VOICES 10 not any voices free 3D functions: (these are defined in THREED.H) NO_3D_FILE 101 Can't open file BAD_3D_HDR 102 Header for file is corrupt/non-existent NO_DRAM_3D 103 Not enough room for this sound NO_3D_HDR 104 No header on this file NOT_BLOCK_DATA 105 data not in block format BAD_FILE_DATA 106 not enough data as header says NOT_INTERLEAVED_DATA 107 data not in interleaved format BAD_3D_FREQ 108 invalid freq. for interleaved 3D data ========= PASCAL: ========= Please see the discussion of the PASCAL error handling techniques at the end of section 1.8.1. ___________________________________________________________________________ 95 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Appendix B - Volume Control Here are the volume ramp control bit definitions: ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Reserved | | | | | | +-------- Reserved | | | | | +------------ Reserved | | | | +---------------- loop enable (0=no loop, 1=loop) | | | +-------------------- bi-direction. loop (1=enable) | | +------------------------ Enable volume ramp IRQ | +---------------------------- Reserved +-------------------------------- Reserved The bits that should be set by the application to get a particular type of volume ramp. The completed value should be supplied to UltraRampVolume and UltraRampLinearVolume. If volume interrupts are enabled, make sure that you have set up a volume interrupt handler (see UltraVolumeHandler). This can be used to create your own multi-point volume envelopes. Bi-directional looping can be used to create a tremelo effect. ___________________________________________________________________________ 96 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Appendix C - Voice Control Here are the voice control bit definitions: ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Rollover when hit ending addr | | | | | | +-------- Reserved | | | | | +------------ data type (0=8 bit 1=16 bit) | | | | +---------------- loop enable (0=no loop, 1=loop) | | | +-------------------- bi-direction. loop (1=enable) | | +------------------------ Enable wavetable IRQ | +---------------------------- Direction (0=inc, 1=dec) +-------------------------------- Reserved The UltraSound is capable of playing back 8 or 16 bit data. (It can only record 8 bit). Stereo is handled by using 2 voices. It can loop on the data in either a uni-directional or bi-directional mode. If you have asked that a particular voice generate a wavetable interrupt when it hits the end of the data (or loop point, if looping is specified), be sure you have specified a wavetable interrupt hander (UltraWaveHander). The mode bits would be constructed and passed to UltraStartVoice and UltraSetLoopMode. The rollover bit is used to tell the software to program the hardware: generate an interrupt when the voice hits the end address, but don't stop playing the voice (or loop if looping is enabled). This allows a voice to continue to play but gives the software a 'marker point' in the playback. This can be very powerfull to allow a seamless playback. The hardware bit for turning rollover on is acutally in the voice's volume control register (there were no spare bits in the voice control register). One of the bits is shared to tell the toolkit software to enable the rollover in the volume control register. ___________________________________________________________________________ 97 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Appendix D - DMA Control Here are the dma to/from DRAM control bit definitions: ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Reserved | | | | | | +-------- 0=write,1=Read | | | | | +------------ Reserved | | | | +---------------- Reserved | | | +-------------------- Reserved | | +------------------------ Reserved | +---------------------------- Data size (0=8 bit, 1=16 bit) +-------------------------------- (1=convert to 2's comp.) Note: The UltraSound DRAM location MUST be on a 32 byte boundary. UltraMemAlloc enforces this stipulation. You define the byte to be passed to UltraUpload and UltraDownload using these bits. To DMA the data out of DRAM, use UltraUpload. To send data to the DRAM, use UltraDownload. Be sure to specify the sample size and whether or not the data is in two's compliment form. The UltraSound can only play data back that is in two's compliment form. If your sample is in one's compliment form, turn on bit 7 when specifying the mode when you call UltraDownload - the data will be translated to two's compliment as it is being DMA'ed. Note: If you are poking data into DRAM, you MUST put the data in twos compliment yourself. This is accomplished by exclusive or-ing the high bit with a 1. ___________________________________________________________________________ 98 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Appendix E - Recording Control Here are the recording control bit definitions: ================================= | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ================================= | | | | | | | | | | | | | | | +---- Reserved | | | | | | +-------- mode (0=mono,1=stereo) | | | | | +------------ Reserved | | | | +---------------- Reserved | | | +-------------------- Reserved | | +------------------------ Reserved | +---------------------------- Reserved +-------------------------------- (1=Convert to 2's comp.) ___________________________________________________________________________ 99 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Appendix F - Patch Files This appendix contains information about the the patch file format. It assumes the reader is familiar with wave table synthesis, music, sound, and the GF1 ASIC chip that is the heart of the UltraSound audio card. The UltraSound patch file is a collection of data structures and sound data in the following format: patch header (# instruments) instrument header 1 (#layers) layer 1 header (# waves) wave 1 header wave 1 data wave 2 header wave 2 data ... wave n header wave n data layer 2 header (# waves) wave 1 header wave 1 data wave 2 header wave 2 data ... wave n header wave n data instrument header 2 (#layers) etc. All of the Advanced Gravis UltraSound patches contain one instrument at the current time. Except for the main patch header, each of the headers has a size field which can be used to seek past unwanted data. For example, if a patch has two instruments and you want to skip the first instrument, you would read the first instrument header, and then skip INSTRUMENT_SIZE bytes. What follows is the patch header definitions given in C and Pascal. After the definitions, a field-by-field breakdown of the headers is given. ====== C: ====== #define ENVELOPES 6 #define HEADER_SIZE 12 #define ID_SIZE 10 #define DESC_SIZE 60 #define RESERVED_SIZE 40 #define PATCH_HEADER_RESERVED_SIZE 36 #define LAYER_RESERVED_SIZE 40 #define PATCH_DATA_RESERVED_SIZE 36 #define GF1_HEADER_TEXT "GF1PATCH110" #define MAX_LAYERS 4 #define INST_NAME_SIZE 16 ___________________________________________________________________________ 100 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ typedef struct { char header[ HEADER_SIZE ]; char gravis_id[ ID_SIZE ]; /* Id = "ID#000002" */ char description[ DESC_SIZE ]; unsigned char instruments; char voices; char channels; unsigned int wave_forms; unsigned int master_volume; unsigned long data_size; char reserved[ PATCH_HEADER_RESERVED_SIZE ]; } PATCHHEADER; typedef struct { unsigned int instrument; char instrument_name[ 16 ]; long instrument_size; char layers; char reserved[ RESERVED_SIZE ]; } INSTRUMENTDATA; typedef struct { char layer_duplicate; char layer; long layer_size; char samples; char reserved[ LAYER_RESERVED_SIZE ]; } LAYERDATA; ... continued on the next page ... ___________________________________________________________________________ 101 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ typedef struct { char wave_name[7]; unsigned char fractions; long wave_size; long start_loop; long end_loop; unsigned int sample_rate; long low_frequency; long high_frequency; long root_frequency; int tune; unsigned char balance; unsigned char envelope_rate[ ENVELOPES ]; unsigned char envelope_offset[ ENVELOPES ]; unsigned char tremolo_sweep; unsigned char tremolo_rate; unsigned char tremolo_depth; unsigned char vibrato_sweep; unsigned char vibrato_rate; unsigned char vibrato_depth; /* bit 0 = 8 or 16 bit wave data. */ /* bit 1 = Signed - Unsigned data. */ /* bit 2 = looping enabled-1. */ /* bit 3 = Set is bidirectional looping. */ /* bit 4 = Set is looping backward. */ /* bit 5 = Turn sustaining on. (Env. pts. 3)*/ /* bit 6 = Enable envelopes - 1 */ char modes; int scale_frequency; unsigned int scale_factor; /* from 0 to 2048 or 0 to 2 */ char reserved[ PATCH_DATA_RESERVED_SIZE ]; } PATCHDATA; ...end of the C section. Please see the next page for the PASCAL section... ___________________________________________________________________________ 102 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ ========= PASCAL: ========= CONST ENVELOPES = 6; HEADER_SIZE = 12; ID_SIZE = 10; DESC_SIZE = 60; RESERVED_SIZE = 40; PATCH_HEADER_RESERVED_SIZE = 36; LAYER_RESERVED_SIZE = 40; PATCH_DATA_RESERVED_SIZE = 36; GF1_HEADER_TEXT : ARRAY[1..12] OF CHAR = 'GF1PATCH110'; TYPE PATCHHEADER = RECORD Header : ARRAY[1..HEADER_SIZE] OF CHAR; Gravis_ID : ARRAY[1..ID_SIZE] OF CHAR; { Id = 'ID#000002' } Description : ARRAY[1..DESC_SIZE] OF CHAR; Instruments : SHORTINT; Voices : SHORTINT; Channels : SHORTINT; Wave_Forms : WORD; Master_Volume : WORD; Data_Size : LONGINT; Reserved : ARRAY[1..PATCH_HEADER_RESERVED_SIZE] OF BYTE; END; INSTRUMENTDATA = RECORD Instrument : WORD; Instrument_Name : ARRAY[1..16] OF CHAR; Instrument_Size : LONGINT; Layers : SHORTINT; Reserved : ARRAY[1..RESERVED_SIZE] OF BYTE; END; LAYERDATA = RECORD Layer_Duplicate : SHORTINT; Layer : SHORTINT; Layer_Size : LONGINT; Samples : SHORTINT; Reserved : ARRAY[1..RESERVED_SIZE] OF BYTE; END; ...continued on the next page... ___________________________________________________________________________ 103 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ PATCHDATA = RECORD Wave_Name : ARRAY[1..7] OF CHAR; Fractions : BYTE; Wave_Size : LONGINT; Start_Loop : LONGINT; End_Loop : LONGINT; Sample_Rate : WORD; Low_Frequency : LONGINT; High_Frequency : LONGINT; Root_Frequency : LONGINT; Tune : INTEGER; Balance : BYTE; Envelope_Rate : ARRAY[1..ENVELOPES] OF BYTE; Envelope_Offset : ARRAY[1..ENVELOPES] OF BYTE; Tremolo_Sweep : BYTE; Tremolo_Rate : BYTE; Tremolo_Depth : BYTE; Vibrato_Sweep : BYTE; Vibrato_Rate : BYTE; Vibrato_Depth : BYTE; { bit 0 = 8 or 16 bit wave data. } { bit 1 = Signed - Unsigned data. } { bit 2 = looping enabled-1. } { bit 3 = Set is bidirectional looping. } { bit 4 = Set is looping backward. } { bit 5 = Turn sustaining on. (Env. pts. 3) } { bit 6 = Enable envelopes - 1 } Modes : SHORTINT; Scale_Frequency : INTEGER; Scale_Factor : WORD; { From 0 to 2048 or 0 to 2 } Reserved : ARRAY[1..PATCH_DATA_RESERVED_SIZE] OF BYTE; END; File Header _________________________________ 'Header' The header field should contain the text "GF1PATCH110." The first 8 bytes will always be GF1PATCH. The next three bytes are the version number of the patch format. As fields are added to the patch, the number will be incremented. All of the UltraSound patches are currently at version 110. The older 100 patches are obsolete and should no longer be in use. 'Description' This description field is usually for copyright information. 'Instruments' The number of instruments in the patch. All of the gravis patches contain only one instrument. ___________________________________________________________________________ 104 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 'Voices' The GF1 synth can update 14 voices at 44.1Khz. As the number of voices increases, the actual output rate of each voice drops. This field should contain the number of voices that were used when creating the patch. This field could be used by a MIDI engine to try and make the patch sound the same regardless of the number of active voices. Currently this feature is not implemented in the gravis MIDI engine. 'Channels' This field is unused. Only mono data can be played out a voice with the GF1. 'Wave_Forms' The total number of waveforms in the patch. This field is used by programs which need to preallocate space for wave form headers before the patch is loaded. 'Master_Volume' This field is currently unused. 'Data_Size' The size of the patch data after it is loaded into GF1 dram. This number includes the space needed to align each of the waveforms on 32 byte boundaries. If you patch loader maintains linked list of waveforms in dram, and you need to use an extra 32 bytes per waveform, then you can use the wave_forms field to figure out how much memory you will need to load the patch. i.e. data_size + (32 * wave_forms). Instrument Header _________________________________ 'Instrument' This field is currently unused. 'Instrument_Name' This field is currently unused. 'Instrument_Size' The size of the instrument. The number of bytes to skip in order to read the next instrument header. 'Layers' The number of layers in this instrument. Multiple layers are usually used in patches where more than one sound is required with a single MIDI event (note on). For example, a piano would could have two layers. The first would be the actual tone from the hammer hitting the strings, and the second would be the (thunk) of the key being struck and all of the mechanisms moving inside the piano. The mechanisms would probably be frequency independant, and also independant of the length of the tone. ___________________________________________________________________________ 105 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Layer Header __________________________ 'Layer_Duplicate' Currently unused. If the layer duplicate is nonzero, then this layer should use the data from the previous layer. Only the headers will follow. 'Layer' The current layer number for this instrument 'Layer_Size' The size of this layer. Can be used to seek past this layer in the file. 'Samples' The number of waveforms in this layer. This field is ignored if layer_duplicate is true. Wave Header _________________________________ 'Wave_Name' This field is currently unused 'Fractions' The start_loop and end_loop are the integer portions of the wavetable address. The GF1 can interpolate between sample points and therefore meore resolution than just the integer address is needed. The most significant four bits are the fractional address for the start_loop, and the least significant four bits are the fractional address for the end_loop. 'Wave_Size' The number of bytes of wave table data that follows -- not # of samples. 'Start_Loop' The integer portion of the starting loop address relative to the beginning of the wave. This address is the relative number of bytes, and not the number of samples. 'End_Loop' The integer portion of the ending loop address relative to the beginning of the wave. This address is the relative number of bytes, and not the number of samples. 'Sample_Rate' This is the sample rate of the recorded data. This number is not related to the actual pitch of the recorded tone. ___________________________________________________________________________ 106 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 'Low_Frequency' Each wave covers a specific frequency range. This is the lowest frequency that this wave can be used to play. This field is scaled be 1000 for accuracy. 'High_Frequency' Each wave covers a specific frequency range. This is the highest frequency that this wave can be used to play. This field is scaled by 1000 for accuracy. If there is another wave adjacent to this one and its range overlaps this range, then the next waveform will always be chosen. 'Root_Frequency' If this wave is played back at the original sample_rate, then this number should be the pitch of the original tone. This field is modified to tune the wave to a particular pitch. This field is scaled by 1000 for accuracy. 'Tune' This field is unused. Tuning is accomplished by modifying the root frequency. 'Balance' 0 is 100% to the right, and 15 is 100% to the left. As the balance is shifted from left to right, the total output power of both channels is constant. 'Envelope_Rate' An array of 6 rates to implement a 6-point envelope. The first three rates can be used for attack and decay. If the sustain flag is set, than the third envelope point will be the sustain point. The last three envelope points are for the release, and an optional "echo" effect. If the last envelope point is left at an audible level, then a sampled release can heard after the last envelope point. The rate values are sent directly to the GF1 hardware, and are described in the volume ramping section. 'Envelope_Offset' An array of 6 offsets to implement a 6-point envelope. The first three offsets can be used for attack and decay. If the sustain flag is set, than the third envelope point will be the sustain point. The last three envelope points are for the release, and an optional "echo" effect. If the last envelope point is left at an audible level, then a sampled release can heard after the last envelope point. The offset values are sent directly to the GF1 hardware, and are described in the volume ramping section. 'Tremolo_Sweep' Not implemented. Tremolo starts automatically when the note sustains. This will be changed in future software to gradually sweep in the tremolo depth from 0 at the rate of tremolo_sweep / 45 seconds. ___________________________________________________________________________ 107 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ 'Tremolo_Rate' The rate of amplitude modulation. 0 is 0.05 Hz, and 255 is 6 Hz. A complete table is listed below. 'Tremolo_Depth' 0 means turn tremolo off. 255 provides a 16 dB modulation. A complete table is listed below. 'Vibrato_Sweep' Gradually sweep in the vibrato depth from 0 at the rate of vibrato_sweep / 45 seconds. 'Vibrato_Rate' The rate of frequency modulation. 0 is 0.05 Hz, and 255 is 6 Hz. 'Vibrato_Depth' 0 means turn vibrato off. 255 is a one octave modulation. 'Modes' A set of bit fields describing modes and data type: BIT 0- 16 bit data BIT 1- unsigned data BIT 2- looping enabled BIT 3- bidirectional loop BIT 4- play patch backwards. start at end address, loop backwards, and then end at beginning address. BIT 5- sustain - enveloping stops at third envelope point. a note off will continue enveloping. BIT 6- currently means enveloping enabled. All gravis patches have this bit set. This field will be modified in the near future to implement a sampled release at note off instead of at the last envelope point. If this bit is on, the sample release occurs after the last envelope point. If this bit is off, the sampled release occurs at the note off. BIT 7- fast release. The last three envelope points are ignored. 'Scale_Frequency' Keyboard frequency scaling. Normally, MIDI note 64 plays a middle C. A 65 plays a C#. frequency scaling changes the distance in pitch of each MIDI note. The scale_frequency is the MIDI note number which is the pivot point for scaling. If scale_frequency is 64, then MIDI note 64 will sound like a C4 regardless of scale factor. 'Scale_Factor' 1024 means normal scaling. Each MIDI note is one semitone away from its neighbor. 512 would be 1/2 semitone apart. 2048 is two semitones apart. ___________________________________________________________________________ 108 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ Appendix G - 3D File Header Here is a definition for the data found in Focal-Point 3D files. All .F3D files will have this format. Both C and PASCAL versions of the definition are listed below. ====== C: ====== /* Bit definitions for tracks that are in a 3D file ... */ #define FRONT_TRACK 0x01 #define RIGHT_TRACK 0x02 #define REAR_TRACK 0x04 #define LEFT_TRACK 0x08 #define ABOVE_TRACK 0x10 #define BELOW_TRACK 0x20 #define FBLOCK_3D 0x01 /* File data is blocked */ #define F16BIT_3D 0x02 /* 16 bit data */ #define FTWOS_CMP_3D 0x04 /* sound is in twos complement form */ #define FLOOPED_3D 0x08 /* sound is looped , not one shot*/ #define FBI_LOOP_3D 0x10 /* sound is bi-directional */ #define F3D_TYPE 0x60 /* Type of 3D sound */ /* types (F3D_TYPE) of 3D sound (only binaural currently supported) */ #define F_BINAURAL 0x20 /* Binaural representation */ #define F_SURROUND 0x40 /* Surround sound (???) */ #define F_QSOUND 0x60 /* Q sound (???) */ /* the following structure is exactly 256 bytes long ... */ typedef struct { char id[10]; /* 3D FILE tag */ int major; /* major version # */ int minor; /* minor version # */ char description[80]; unsigned int type; /* See above .... */ int tracks; /* tracks included in this file ... */ int reserve1[24]; /* for expansion */ int maxvol; /* volume can range from 0 to maxvol */ int reserve2[10]; /* for expansion */ unsigned long blocksize; /* # of bytes in block data */ unsigned long loop_offset; /* byte offset to where loop begins */ unsigned long reserve3[9]; /* for expansion */ unsigned long frequency; /* initial playback frequency */ unsigned long reserve4[10]; /* for expansion */ } FILEHDR_3D; ...end of the C section. Please see the next page for the PASCAL section... ___________________________________________________________________________ 109 Ultrasound Software Development Kit (SDK) Version 2.10 ___________________________________________________________________________ ======== PASCAL ======== CONST { Bit definitions FOR tracks that are in a 3D FILE ... } FRONT_TRACK = $01; RIGHT_TRACK = $02; REAR_TRACK = $04; LEFT_TRACK = $08; ABOVE_TRACK = $10; BELOW_TRACK = $20; FBLOCK_3D = $01; { FILE data is blocked, NOT interleaved } F16BIT_3D = $02; { 16 bit data } FTWOS_CMP_3D = $04; { sound is in twos complement form } FLOOPED_3D = $08; { sound is looped , NOT one shot } FBI_LOOP_3D = $10; { sound is bi-directional } F3D_TYPE = $60; { TYPE OF 3D sound } { types (F3D_TYPE) OF 3D sound (only binaural currently supported) } F_BINAURAL = $20; { Binaural representation } F_SURROUND = $40; { Surround sound (???) } F_QSOUND = $60; { Q sound (???) } TYPE FILEHDR_3D = RECORD ID : ARRAY[0..9] OF CHAR; { 3D FILE tag } Major : INTEGER; { Major version # } Minor : INTEGER; { Minor version # } Description : ARRAY[0..79] OF CHAR; SType : WORD; { See above } Tracks : INTEGER; { Tracks included } Reserve1 : ARRAY[0..23] OF INTEGER; { for expansion } MaxVol : INTEGER; { volume range 0-maxvol } Reserve2 : ARRAY[0..9] OF INTEGER; { for expansion } BlockSize : LONGINT; { # of bytes in block data } Loop_Offset : LONGINT; { byte offset of loop begin} Reserve3 : ARRAY[0..8] OF LONGINT; { for expansion } Frequency : LONGINT; { playback frequency } Reserve4 : ARRAY[0..9] OF LONGINT; { for expansion } END; { the file header is exactly 256 bytes long } ___________________________________________________________________________ 110