7#,8) 33v`L8x\ |K*uK KKK73KKKKKKThe SoundFont Enabler Software Package: An Overview NOTE This document is written assuming the reader understands the C and C++ programming languages, the basics of WaveTable synthesizers, the MIDI protocol, the RIFF file format, and the basic properties of the SoundFont file format for sound data. Abstract The SoundFont Enabler Software Package is a collection of C and C++ routines and objects designed with two purposes in mind: (1) to demonstrate to the experienced software developer how to create SoundFont compatible software, while at the same time allowing them to prototype SoundFont compatibility in their synthesizer and (2) to produce the CORRECT vectors of articulation data for a given SoundFont Note-On event. To achieve (1), example software demonstrating how to read SoundFont data from a file as well as example software demonstrating how to navigate a SoundFont bank given a particular Note-On event is provided. The software extracts the data from the file directly into memory and operates upon it as the SoundFont specification dictates. No pre-processing is done to the data structure format nor the articulation data itself, with the exception of byte swapping necessary for Big Endian systems. All software is written in a platform independent ANSI/ARM conforming manner with modularity in mind using easily understood methods. This software is by no means optimized for the speed which a WaveTable synthesizer must process data, nor is it written with any one particular operating system in mind. The software is known to compile and execute successfully using commercial compilers for the following operating systems: DOS, Windows 3.1, Macintosh System 7, and Sun OS 4.3. To achieve (2), the SoundFont Enabler comes with a main routine which provides a menu-driven interface to the SoundFont Enabler functions as well as an option to dump vectors of articulation data to the screen and to a file. The SoundFont Enabler also comes with a collection of sample SoundFont banks called the "SoundFont Compatibility Validation Suite." This collection of banks includes a General MIDI SoundFont bank for audio comparison as well as diagnostic SoundFont banks designed for software testing purposes. The software developer should first review and perhaps even compile and step through the given source code to facilitate the understanding of the workings of the Enabler and the SoundFont format itself. Then the software developer should build SoundFont compatible synthesizer drivers using any speed or size optimizations or any other changes necessary in order promote efficiency under their particular platform. The developer may choose to pre-process all SoundFont data into a data format more native to their synthesizer, pre-perform operations upon the data which are unnecessary for every note-on event, modify, or even re-write the Enabler code to make it more efficient in their environment. Finally, the software developer should run their software on the Validation Suite of banks and compare output vector data to the output of the Enabler software itself in order to verify accuracy of their SoundFont compatible software. The SoundFont Enabler is SoundFont 2.0 compatible only. It will not correctly read or produce valid output vectors for SoundFont 1.0 banks. The SoundFont Enabler Software Description Location The SoundFont Enabler software is located in the following subdirectories: include: contains header files src: contains source code files Compilation The SoundFont Enabler software can be compiled with ANY ANSI and ARM conforming C++ compiler. The software is designed to be compiled into a single library whose API methods may be utilized by a client. There are a few compile flags which MUST be GLOBALLY set in order to compile the software. These flags specifies the Endian nature of the system, and also make special provisions for memory allocation and/or File I/O for DOS, Windows, and Macintosh System 7. Endian nature is set with ONE of the following flags: __BYTE_COHERENT: Compile the code for Little Endian systems __BYTE_INCOHERENT: Compile the code for Big Endian systems Memory Allocation methodology is set with ONE of the following flags: CPP_MEMORY: Always use 'new' and 'delete' DOS_MEMORY: Use farmalloc and farfree for > 64kB memory blocks EMU_WINDOWS: Allocate and lock > 64 kB memory blocks from the Global Heap. This flag also automatically adds as a standard header file to be looked at before all others. For details, see The Memory Allocation Object below. If the software is intended to be compiled on a Macintosh based system, there is a THIRD Global compile flag must be set to tell the software about the existance of the Macintosh specific File I/O structures and routines, and to look for the header file. This flag is called: USE_MACINTOSH This code has been successfully compiled on a Macintosh and used in Macintosh Applications, and it has been compiled as a Code Resource. To compile the SoundFont Enabler as a Code Resource on a 680x0 based Macintosh computer, the compiler must be able to create object code which uses A4 relative global variable addressing. Either Symantec C++ or Metrowerks Code Warrior may be used to do this. Macintosh developers should also note that USE_MACINTOSH may also be used as a switch for Memory allocation if they do some code writing in The Memory Allocation Object. Currently, the The Memory Allocation Object has no SPECIAL equivalent memory allocation routines specifically written for Macintosh environment because the new and delete calls were always sufficient for allocating large contiguous blocks of memory. However there are more optimized Macintosh specific ways of allocating memory which the Macintosh developer may want to use when using this software. For simple prototype purposes, following files should be compiled into a single library called "The SoundFont Enabler Library": src/enab.cpp src/hydra.cpp src/omega.cpp src/riff.cpp src/sfdetect.cpp src/sflookup.cpp src/sfnav.cpp src/sfreader.cpp src/win_mem.cpp The API to this Enabler Library is described in The SoundFont Enabler API below. An application with a main() routine may be created or modified such that it uses that API and links in the Enabler library. An example main file called enabdrv.cpp gives a menu driven interface to the Enabler functions. With it one can load SoundFont banks, produce vectors for note-on events, and save those vectors in a file or display them on the screen. The Data Types The SoundFont Enabler software does not use any naked data types. The basic datatypes are defined in the file "datatype.h". These datatypes are compatible with the Windows datatype names. Those are: CHAR: an 8 bit signed value BYTE: an 8 bit unsigned value SHORT: a 16 bit signed value WORD: a 16 bit unsigned value LONG: a 32 bit signed value DWORD: a 32 bit unsigned value The Enabler software does not use any floating point arithmetic. Note that datatype.h also detects incompatible systems at COMPILE time by checking the system's limits of the naked datatypes (in the system's copy of ) which are typedefined into the above with constant values. If those limits do not match, the software designer must change the typedefs to use data types which match. If equivalent data types are not available on the system being used, the Enabler software cannot be easily compiled in the desired environment without modification. Finally, datatype.h provides unions to extract or set the individual BYTES or WORDS of 16 or 32 bit data values. These are convenient since they can only otherwise be extracted or set using inefficient bit shifting operations. The SoundFont Enabler Object The SoundFont Enabler object is a collection of C++ functions, defined in include/enab.h with methods in src/enab.cpp, which provides the fundamental interface to the SoundFont Enabler. It contains methods to Load and Unload SoundFont banks, and to Navigate a single Note-On event. The SoundFont Enabler object API contains all of the functions necessary to implement SoundFont compatibility in a working wavetable synthesizer. Note that it does NOT contain functions to parse MIDI data or to program any specific synthesizers or audio DSPs. The synthesizer designer can simply give SoundFont bank load and MIDI Note-On information into the various API functions and receive vectors of SoundFont data. The synthesizer designer may then program their synthesizer with values translated from the given SoundFont data. This module defines utility routines to load, navigate, and obtain various pieces of information from SoundFont banks. Collectively known as the "Enabler", these routines can be used to prototype SoundFont playback implementations. Functionality exists to load the articulation portion (preset data) of a SoundFont bank into main system memory, navigate the data upon a MIDI note-on event, and return the SoundFont parameters gathered for that particular event. The units are those native to SoundFont units and should be translated by the client software into values digestible by the target sound engine. That is, once navigated, the rest is up to the client caller. Sampled wave form data is left in the source file. The client may must write routines to transfer the sample data to their target sound engine. They must then update the in memory articulation data to reflect where the data has been placed. Routines exist in this module to find the location and size of any particular single sampled waveform (or "sample") within the source file, and how big it is. Armed with this knowledge the client can write the routines to download all of a banks sample data. For instance, a simple method might be to write all sample data to the dedicated sound engine RAM, and add a constant offset to all the sample address in the bank's Sample Headers. The Sample Headers is the list of data structures within the articulation data of a SoundFont bank which define basic sample parameters including as loop points. By definition, the Sample Headers are 'normalized' to the source file, zero'ed to the Sample Chunk Offset. (i.e., the first Sample has a dwStart value of zero). Functionality exists in this module to obtain the Sample Headers of the articulation data, as well as obtaining the SampleCkOffset from the src File. Once you know where the sample chunk starts, and have the i'th sample hdr, a quick fseek() will take you to the first sample element of the i'th sampled wave form data. Next, once you write the sampled wave form data to its new home you know the new 'real' address and, having the Sample Header in front of you, you can update the start, end, and loop points of that Sample Header. During the navigation of the Note On Event, you are returned these new address' in the SoundFont vector. See the routines sfGetSampleHDdrs() and sfGetSampleCkOffset prologues for additional details. The SoundFont Vectors The object of the SoundFont Enabler is to produce a valid series of "vectors" for a given SoundFont bank and a given Note-On event. A vector is a data structure which contains the final values of the SoundFont Generators or Modulators which are collected from the SoundFont bank represented by the combination of the various key and velocity ranges, the individual layers and splits, and the Preset Layering action. A single vector of generators and modulators is to be be applied to a single sample. When a multi-sampled preset (a preset where a single key triggers 2 or more samples to be played back) is found, the output of the Enabler will be ARRAYs of these vectors. The data structure definitions for the the vectors are in include/sfdata.h. The user will note that currently only the Generator vector is produced by the current version of the SoundFont Enabler and that the Modulator vector is not. In fact the Modulator vector is not even defined within this version of the SoundFont Enabler. The SoundFont Enabler Application Programmer Interface (API) The following describes the Enabler API. This information may also be found in include/enab.h sfBankID sfReadSFBFile(CHAR *bankFileName); Load the given bank denoted by 'bankFileName'. A bank id of -1 is never valid, and indicates an error during load. Unique bank ids are returned for each bank concurrently loaded. There can never be more than MAXLOADEDBANKS loaded at once. SHORT sfUnloadSFBank( sfBankID whichBank ); Unload the (assumed) loaded SoundFont Bank. SFSAMPLEHDRPTR sfGetSampHdrs( sfBankID fromBank, WORD *cnt ); This routine will return a pointer to the list of Sample Headers within the given loaded SoundFont bank with ID 'fromBank'. The sample Hdrs take the form of an array of size 'cnt`. The formal cnt is pre-allocated by the caller and written by this routine as the extent of the vector of hdrs returned as the routine's "function result". A single Sample Header data structure has the following form: (see include/hydra.h) typedef struct sfSampleHdrTag { CHAR achSampleName[SAMPLENAMESIZE]; // size 20 DWORD dwStart; // Sampleaddress DWORD dwEnd; DWORD dwStartloop; DWORD dwEndloop; ... } sfSampleHdr; *Note: The address of these hdrs are given in units of Samples (i.e., 16 bit WORDs) such that ((dwEnd - dwStart)+1) * 2) yields sample size in bytes. (Useful when reading the sample data from the src file, given sampleOffset returned by sfGetSMPLOffset() below...) *Warning: The formal return value is a typedef'ed pointer to the SoundFont bank's in memory articulation data called the SampleHdrs array. An alias'ed pointer results, these are the keys to the car, don't delete it. Drive safely. Update these address values, thus "normalizing" the address to your sound memory, whereever that might be. Return Values: SUCCESS = non null "function" result, and formal cnt > 0; NULL on error, DWORD sfGetSMPLOffset( sfBankID fromBank ); Returns the offset to the beginning of the sampleData 'chunk' in 'fromBank's' src file. Units are in bytes, such that a call such as fseek to the value returned from SEEK_SET followed by an fseek to some (samps[i].dwStart*2), from SEEK_CUR, (see sfGetSampHdrs()) will position the current file pointer to the first sample in the sampled wave form data for the i'th sample. Each Source bank loaded will have a different sample chunk offset value. Once read and placed in memory by the client, the sample address can be updated to reflect their new location. As a result of sfNavigate(), the SoundFont vector(s) will also contain these sample address values. WARNING: If the sfSampleType element of the Sample Header data structure has the 16th bit SET (IE if ((hf->pSHdr[i].sfSampleType & 0x8000) == 0x8000) that is an indication that the sample is NOT IN the SoundFont bank! Do NOT attempt to read sample data for that Sample Header from the file! Return Values: N where N is non-zero. 0 (zero) on error. SFPRESETHDRPTR sfGetPresetHdrs( sfBankID fromBank, WORD *cnt ); This routine returns a pointer to the list of Preset Header data structures within the given SoundFont bank with ID 'fromBank'. A single Preset Header contains the preset name, MIDI preset number, and MIDI Bank number, among other things. The Preset Headers are array of 'cnt' data structures. The 'cnt' variable must be a pre-allocated WORD buffer to receive the actual size of the returned array. The Preset Header data structure is reproduced below for convenience, see hydra.h for the complete definition. typedef struct { CHAR achPresetName[PRESETNAMESIZE]; // Size 20 WORD wPresetNum; // The MIDI preset number. WORD wPresetBank; // The MIDI bank number. ... } sfPresetHdr; Return Values: Address of preset headers (descriptors) on SUCCESS, cnt is the extent. NULL on error; sfData* sfNav(sfBankID sfBank, WORD MidiBank, WORD MidiPreset, WORD MidiNote, WORD KeyVelo, WORD *cnt ); Given an Enabler bankID, MIDI bank number, MIDI preset number, MIDI key number, and MIDI velocity (collectively a note-on event), this routine will navigate the 'sfPreset' in 'sfBank' and return an array of "vectors" containing SoundFont parameters. The vector array has an extent of 'cnt' elements. The 'cnt' formal is a user allocated buffer of size sizeof(WORD) that is available for writing by this routine. There will be 'cnt' elements of type sfData in the returned array. The sfData type is found in sfdata.h and due to its size, is not reproduced here. The values in the sfdata vector are in SoundFont units and must be translated by the caller into units appropriate to the target sound engine. *Note: SoundFont banks CAN contain multiple MIDI banks or portions of several MIDI Banks. Don't confuse the SoundFont Bank and MIDI Bank formals. If you ask for a MIDI Bank not found in the SoundFont Bank, a ENOSUCHPRESET error is returned since the MIDI Bank and MIDI Preset two-tuple is not found in any of SoundFont Bank's Preset Headers. See the routine sfGetPresetHdrs() to pre-determine if your MIDI bank/MIDI preset 'patch' is present in the given SoundFont Bank. Return Values: Address of a static memory area of sfData structures. 'cnt' returns the current number of valid elements in the area. The number of elements corresponds to the number of voices, or oscillators in the definition of the preset given the particular note on event. NULL on error. void sfDumpBanksInfo(void); This routine will dump (to stdout) the internal bank descriptors used by the 'Enabler' to track the different banks that may be loaded at any given time. Note that it is possible to load the same SoundFont bank more than once! The info for each currently loaded bank: FileName of the banks src file SoundFont bank name Enabler ID assigned to the bank Sample Ck Offset value (in bytes) of FileName enaErrors sfGetError(void); While not extensive, there are a few error values defined in this module. In general, if routines return a -1, (or NULL for those that return PTRs) some sort of error has been encountered, this routine will return the last error value. You may clear the error prior to a call using the sfClearError() routine. void sfClearError(void); 'clear' an error condition if desired. The Underlying Software Methods The Enabler uses various methods to accomplish its tasks. The source code to those software objects are also provided here. Included are methods to read RIFF files, methods to extract SoundFont data from a SoundFont bank, methods to navigate the SoundFont bank to produce output vectors for a single Note-On event, and other miscellaneous functions. A brief description of those objects follows. The various header files prototype all of the objects below, and comments in the source code go into more detail about how methods are working and what types of platform issues may arise. You may note that the functionality provided by the Enabler library does not span the functionality provided by the sum of all of the underlying objects. The HydraClass Most of the software below operates on a single object which represents the articulation data of the SoundFont bank. This object is called the HydraClass. The Hydra is a mythological 9 headed beast, the articulation data in a SoundFont bank is based on 9 static arrays of data. The HydraClass is defined in the file include/hydra.h and has methods in src/hydra.cpp. The class contains place holders or pointers for each of the 9 SoundFont articulation data lists, variables for informational purposes, and some methods. There are methods to assign values to its informational elements (such as Bank Name, File Format Version number, and others) and a single method to erase and/or initialize all variable data space memory. The HydraClass does NOT contain methods to FILL the data in the 9 SoundFont lists. This is because that data may be either read from a SoundFont file or CREATED by either an SoundFont editor or a tool which imports another sound file format into SoundFont format. Although the SoundFont Enabler only gets data from a SoundFont file, the forthcoming SoundFont Edit Engine WILL allow creating or importing data for those lists. The RIFF Object The RIFF object is a C++ class, defined in include/riff.h with methods in src/riff.cpp, which contains the various methods necessary for navigating RIFF files. This is the ONLY object in the SoundFont Enabler which actually opens RIFF files and manipulates the file data. (NOTE: the SoundFont Enabler DRIVER does create files, but not SoundFont or RIFF files). RIFF contains only basic methods to move through chunks, and to extract and gather information from those chunks. Note that all Low Level File I/O operations are done within a single set of routines, namely: RIFFOpen RIFFClose RIFFSeek RIFFTell RIFFRead Those routines perform the specific File I/O operation. The public functions used for opening a RIFF file are both called OpenRIFF() A RIFF file may be opened using EITHER a filename or a Macintosh File System Specifier (FSSpec) structure pointer. When a file is opened with one of those tokens, some of the private object variables are set such that when the above mentioned generic File I/O routines are called, the appropriate low level File I/O functions are called. Opening a SoundFont using a filename causes the object use the ANSI defined (fopen, ftell, etc) File I/O routines. Opening a SoundFont using the FSSpec pointer causes the object to use the Macintosh (FSpOpenDF, GetFPos, etc) File I/O routines. Note that by localizing all low level file manipulation calls to this set of routines, the RIFF class may easily be altered so that the SoundFont data would be accessible from different file systems or even different storage media, so long as there is a way to open, close, get current position, set current position, and read data from the source medium. If an attempt is made to use OpenRIFF to access a file which is NOT a RIFF file, OpenRIFF fails with an error condition. Navigation routines are used to move within a RIFF file. Movement must take place along the chunks themselves, and always leaves the current position at the beginning of the chunk DATA, NOT the beginning of the chunk information! The RIFF navigation routines provided are: Ascend(): Move from the current RIFF chunk to the next RIFF chunk Descend(): descend from the current RIFF chunk into a RIFF sub chunk if one exists FindCk(): find a RIFF chunk with a particular token name ReadCkHdr(): get information from the current chunk Reset(): go back to the beginning of the RIFF data The RIFF chunk information extraction methods provided are: GetRIFFSize(): Tell the total size of the RIFF file data GetCkID(): Get the RIFF token value of the current RIFF chunk GetCkFormID(): If the current RIFF chunk is a LIST, get the LIST token value GetCkSize(): Get the size of the current chunk IN BYTES. To extract the RIFF chunk data itself, RIFFRead is used. Most routines do not return with an error code, however error codes are stored and can be retrieved by a future call. The error code extraction routine is GetError() GetError will provide the error details of the previous operation, calling GetError itself will NOT change the stored error code to SUCCESS (meaning it won't tell you when you successfully call GetError). The SoundFont Reader Object The SoundFont Reader object is a C++ class, defined in include/sfreader.h with methods in src/sfreader.cpp, contains the various methods necessary for reading SoundFont data itself. This object's main purpose is to use the RIFF object for navigation and extraction of the SoundFont data from the file into the HydraClass. Like the RIFF object, the SoundFont reader object uses either a filename or an FSSpec pointer to select a SoundFont bank. The main public function in this object is: ReadSFBFile() This function opens the file using the RIFF object, then verifies that the file is a SoundFont bank (a RIFF file subtype 'sfnt'). The version of the SoundFont bank is checked (must be version 2.x), then the various data is extracted. A HydraClass is allocated from the heap by sfReader and its contents are filled, and data space for the various INFO chunk data is also allocated from the heap and filled. (Note: see The Memory Allocation Object for details on memory allocation) A pointer to the HydraClass is returned upon the successful reading of the SoundFont file data. Note that it is up to the CLIENT to free the HydraClass memory when they are through with it! The INFO chunk data is contained within the class, methods are provided to extract the information (GetInfoSubCkVal), and the memory allocated for that data is freed when the Reader object is freed. ReadSFBFile also converts the memory image of the file data from Little Endian to Big Endian format IF the software was compiled with the __BYTE_INCOHERENT flag. Finally, ReadSFBFile contains an optional parameter for a wavetable ROM ID. If this parameter is used, a distinct check for a wavetable ROM ID match is made where IF the SoundFont file has an expected wavetable ROM ID, and IF some of the samples are ROM samples, and IF there is NOT a match between the two IDs, the routine fails. The routine also fails if it detects bad file format or runs out of dynamic memory space. Methods are also provided for sample data extraction, namely: SetupToFillSampleBuckets() FillSampleBucket() Two versions of Setup are provided, one tells the reader that ALL sample data is to be extracted, the other tells the reader that only SOME of the sample data is to be extracted. For the latter, the limits are set with the values IN WORDS of the sample start and end point, RELATIVE to the first sample point. That allows the routine to work with the loop point data values which are contained within the sampleHeader information. With FillSampleBucket, a pointer to a size value is passed in along with a pointer to a buffer. The the size value is specified in a DWORD variable by the function caller and a POINTER to that DWORD is passed into the function. The data is read into the buffer, and the amount of data which ACTUALLY gets read is put into the size value. The buckets are generally extracted until the limits of the Setup function are reached. After the limits are reached and all data was read once, no data is extracted upon additional calls to this routine, and the size value is set to 0. Also note that when the LAST bucket is filled, the size value returned will likely not be the size value passed in. Either of these conditions should inform the client that the operation is completed. Like the RIFF object, a method called GetError() is provided to retrieve error codes. The SoundFont Error Detection Object The SoundFont Error Detection object is a C++ class, defined in include/sfdetect.h with methods in src/sfdetect.cpp, contains the various methods necessary for detecting errors within SoundFont data. For the purposes of the Enabler, only a simple SoundFont data error detection facility is provided. However the forthcoming SoundFont Verifier will contain a more elaborate yet backwards compatible version of this object with SoundFont error reporting facilities. The class currently contains a single routine called VerifySFBData() This routine currently detects of range list indices, which would cause the Navigator to read from or write to incorrect memory space. (See The SoundFont Navigator Object). The SoundFont Navigator Object The SoundFont Navigator object is a C++ class, defined in include/sfnav.h with methods in src/sfnav.cpp, contains the various methods necessary to extract vectors of SoundFont data appropriate for a given SoundFont, Bank Index, Preset Index, Key Number, and Velocity number from a HydraClass. The SoundFont Navigation routines are split up as follows: SetHydraFont(): Set the navigator to work with the given HydraClass GetSFNum(): Given a Bank and Preset index, return a Preset Header index Navigate(): Given a Preset Header index, Key number, and Velocity number, produce the output vectors. SetHydraFont() is a separate routine because it should only be called upon a change in SoundFont reference, such as when a SoundFont is freed from memory and ANOTHER SoundFont is loaded into memory. It takes a pointer to a HydraClass as a parameter, and returns a pointer to the PREVIOUSLY set HydraClass structure. GetSFNum() is a separate routine because only should be called upon a change in Preset Index reference, such as when a MIDI Program Select message is received. It takes a MIDI bank index and MIDI preset index and a POINTER to the Preset Header Index value. This pointer must point to memory allocated by the client. A return of 0 indicates all is well, otherwise the HydraClass contains no Preset Header list entry with the given MIDI bank and preset indices. Navigate() does most of the work. The client passes in the index to the Preset Header list entry (obtained by calling GetSFNum), the desired MIDI key number, and the desired MIDI velocity value. The navigation routine then begins. The Navigator starts off by collecting the data within the valid ranges in the Preset level. This continues until an Instrument index is found. Then the data within the given Instrument is found until a Sample index is found. The Layer parameters are added to the Split parameters, the sample links are established, and the navigation continues with the next vector. This process continues until the data from all appropriate splits in all appropriate layers have been collected and processed, OR until the MAXIMUM allowed number of Sample indices are found. For specific details, see the SoundFont file format specification and the commented source code itself. Once the vectors are generated, the client makes explicit calls to get the output vectors themselves. Those methods are: GetNumOsc(): Return the NUMBER of vectors produced by the last navigate call GetSFData(): Return a pointer to the FIRST vector produced by the last navigate call The Memory Allocation Object The Memory Allocation object is a collection of C++ functions, defined in include/win_mem.h with methods in src/win_mem.cpp, which contains the various methods necessary to handle memory allocation, de-allocation, allocated memory initialization, and allocated memory copying on various operating systems using various memory archetectures. The Enabler requires the use of contiguous memory. In many instances, specifically in the SoundFont data lists, this contiguous memory is very large in size, meaning greater than 64 kBytes. For some segmented archetectures, specific memory allocation routines are required to allocate (and "lock") such large chunks of contiguous memory. When the Enabler needs a contiguous block of memory which MAY OR MAY NOT be greater than 64 kBytes in size, the routines within the Memory Allocation object are used. When the Enabler needs memory which should NEVER exceed 64 kBytes, it safely uses the standard C++ techniques (new, delete, etc) to acquire it. Since these architectures each have their own system specific routines for this purpose, compile flags are used to select one set of routines. The programmer MUST set ONE of these flags as a global compile flag since the various objects may use either HUGE pointers or normal pointers in certain circumstances. Each set of routines uses the SAME function prototypes. This allows the software to be easily portable. The routines are called: Alloc(): Takes a DWORD, allocates that much contiguous space, returns a pointer Dealloc(): Takes a pointer and frees it up Memcpy(): Takes two pointers and a size, copies data FROM the second pointer's memory TO the first pointer's memory HFMemSet(): Takes a pointer and a size, initializes all data to 0 Compile flags exist to accommodate the following: Windows 3.1: Alloc() uses GlobalAlloc() and GlobalLock() Dealloc() uses GlobalUnlock() and GlobalFree() DOS: Alloc() uses farmalloc() Dealloc() uses farfree() Non segmented architecture Alloc() uses 'new' Dealloc() uses 'delete' The software developer can continue this paradigm for whatever operating system they are using by establishing an equivalent set of routines, establishing a new set of compile-flag switch able functions, and having the generic functions appropriately use the new set of routines. An IMPORTANT note is that when an application uses Alloc to allocate memory, that application MUST use Dealloc to free it! Mixing paradigms here (Alloc-delete, GlobalAlloc-Dealloc) is EXTREMELY dangerous especially when used in cross platform code. Miscellaneous Objects An object called sflookup is provided, which provides a quick SFEnum to SFVector Element map. It establishes a lookup table such that if you recast the SoundFont vector as an array of SHORTs, and assigned the memory offset from the vector's starting point by the lookup table entry value indexed by the SoundFont generator enumeration value, you will access the appropriate section of the vector. To understand the purpose of this utility, consider the following detection and assignment code: if (currGen == keynum) vector->shKeyNum = currGenVal; else if (currGen == velocity) vector->shVelocity = currGenVal; else if ... /* on and on with lots of lines of code */ This would be more efficiently implemented as follows: *(SHORT *)((BYTE *)vector + sfLookup[currGen]) = currGenVal; The Navigator takes advantage of this to avoid large the switch/case statements. The offset values in the sfLookup table are generated dynamically at runtime. This means they are not constant values, they are the true memory image offsets of the data structure. This assures that the operation will work on any system with any bit boundary, even if the system or the system's compiler re-orders the elements of the data structure. An object called omega is provided and used as the basic object for storing and retrieving the error codes. The Validation Suite of SoundFont Banks Thus suite of SoundFont banks is to be used to verify the correct operation of SoundFont system software. This is done by testing with a broadly familiar General Midi SoundFont bank (i.e. GM.SF2), by testing with simple sounds to isolate driver conversion problems (i.e. TEST.SF2), and testing with complex SoundFont structures (i.e. LAYERED.SF2) to verify the correctness of the SoundFont navigator. These banks are complete with samples and do not reference ROM samples. These banks are "Copyright E-mu Inc. 1995". License is hereby granted by the copyright holder to copy and use this bank for the purpose of verifying SoundFont() compatibility. Each of these banks are described below. GM.SF2 This is the standard SoundFont 1MB General Midi bank containing samples for presets 0-127 and the standard drum kit. GM midi sequences can be played to verify the SoundFont implementation. TEST.SF2 The following presets are provided to test individual parameters: 0 LFO to Filter 1 Envelope to Filter 2 Volume and Filter Envelope 3 Envelope to Pitch 4 Lfo to Pitch (slow Lfo) 5 Lfo to Pitch ( fast Lfo) 6 Warm Synth String Pad 7 Warm Synth String Pad with Chorus 8 Warm Synth String Pad ( with Envelope to pitch and filter) 9 Synth Drum 10 %th Saw Wave 11 Bass & Lead 12 Warm Pad 13 Warm Pad Lfo 14 Warm Pad Env 15 Bass & Lead 1 15 Bass & Lead 2 15 Bass & Lead 3 LAYERED.SF2 The following presets are provided to test individual parameters: 0 Major 7th Chord 1 Organ 1 2 Bass/ Piano 3 Velocity/Key Splits 4 Strings Return Values: Value other than -1 indicates a successful operation -1 indicates an error457;=/19 "$-  'D)9U%,%/+++,,.///00<0=0001111112222Y2[2]44457585977#888081828899<.<1u     $$  00S>u>v?Y?\??@y@{EEFLFNFjGQGSHHH4IoIqIIIIIILLQRQaVV]]acafagamanaxayajkmmmmwx(;C_@B|}~ $$ @     T567<=01;<VW { | i j #$-.y EF Xespq! !! ! ! ! ! ! !! !M!"cdST789VW q r""#s#t%,%/'#'$++,,///00=0>00001111222[2]444'4-4f44455$58596I6J6U6W77777888182::;;ؿ! ! ! ! ! ! ! ! ! !! ! ! K;;;<<0<1u>v>>>????F?G?Y?\?l?m?????@ @)@E@`@y@{C@CAEEE/E0F<F=FMFNFlFmGRGSG}G~GGGHHH6H8IoIqIIIIIIK7K8LLLLMMOOQQQRQbQcS>S?SSSSSST! !! ! ! ! ! ! UTTTHTSTTWWXxXyYYZ ZYZZZZ[8[v[[[\6\7\\\]]]]_____aac(c)eeeqereeeeggjjjjkkkkllmm,m-mmmmo"o#o^oopPpQqqsZs[tBtCvrvsvv!  ! ! ! ! ! !  ! ! SvwUwwwxxylym{{}}}~~.~~~%Q)*1Rp?@^_ABISTdyXeu! ! !! ! ! ! ! U3EO]s}~c !VZu'(89GXk01Xhef'IfFv,V(\>j! !}! ! !  @! ! Wjmk Return Values: 0 indicates successful operation Other indicates an error Return Values: Returns a non NULL value upon success NULL indicates an error Return Values: Returns a non zero value upon success 0 indicates an error Return Values: Returns a non NULL value upon success NULL indicates an error Return Values: Returns a non NULL value upon success NULL indicates an error Return Values: Returns an enabler error code. See below. The following are the error codes: typedef enum { enaSUCCESS, enaLOADERROR, enaENOSUCHBANK, enaENOSUCHPRESET, enaEBANKTABLEFULL, enaWBANKNOTLOADED, enaEBANKALREADYLOADED, enaVERIFYERROR }enaErrors; This suite of SoundFontForewordandcurrently the vectorcontains gvalues. BASIC This SoundFont bank is designed to test individual parameters and basic functions of a SoundFont bank.BASIC.SF2 is contains all sample data required to play back the bank. The bank is split up into a num3 !UYuv'( !)*/x2r%Yfghj.?FNXce H4  $$  @ Vber of PRESETS each lying under MIDI Bank index 0. Each PRESET is designed to test individual parameters or groups of parameters. Basically, the presets get more complicated as their indices increase. This SoundFont bank does not use Layers for any reason other than to point at instruments. Individual samples are shared between the presets. Preset index : A rough diagram of how this bank looks would be the following:  This is the simplest form of a SoundFont Object or Preset. The Preset points to a single instrument, which in turn points to a single sample. The Layer serves no purpose but to point to the instrument. The instrument has a few generator parameters and points to a single sample. Upon running the SoundFont Enabler provided, and giving a keynumber of 60 and a velocity of 100, the output will be a single vector with the following parameters: --------- Vector # 0 -------------- dwStart 4888 samples dwEnd 7025 samples dwStartLoop 6883 samples dwEndloop 7017 samples dwSampleRate 44100 Hz OriginalKey 64 Correction 60 cents shModLfoToPitch 0 cents/max excursion shVibLfoToPitch 0 cents/max excursion shModEnvToPitch 0 cents/max excursion shInitialFilterFc 8246 cents above 8.176 Hz shInitialFilterQ 180 cB above DC level shModLfoToFilterFc -1200 cents/max excursion shModEnvToFilterFc 0 cents/max excursion shModLfoToVolume 0 cB/max excursion shChorusEffectsSend 0 units of 0.1% shReverbEffectsSend 0 units of 0.1% shPanEffectsSend 0 units of 0.1% right shDelayModLfo -12000 TimeCents above 1 second shFreqModLfo -1908 cents above 8.176 Hz shDelayVibLfo -12000 TimeCents above 1 second shFreqVibLfo -725 cents above 8.176 Hz shDelayModEnv -12000 TimeCents above 1 second shAttackModEnv -12000 TimeCents above 1 second shHoldModEnv -12000 TimeCents above 1 second shDecayModEnv 768 TimeCents above 1 second shSustainModEnv 0 cB Attenuation shReleaseModEnv -12000 TimeCents above 1 second shAutoHoldModEnv 0 TimeCents / KeyNum shAutoDecayModEnv 0 TimeCents / KeyNum shDelayVolEnv -12000 TimeCents above 1 second shAttackVolEnv -12000 TimeCents above 1 second shHoldVolEnv -12000 TimeCents above 1 second shDecayVolEnv -12000 TimeCents above 1 second shSustainVolEnv 0 cB Attenuation shReleaseVolEnv -12000 TimeCents above 1 second shAutoHoldVolEnv 0 TimeCents / KeyNum shAutoDecayVolEnv 0 TimeCents / KeyNum shKeynum 60 shVelocity 100 shInstVol 82 cB of Attenuation shCoarseTune 0 Semitones shFineTune 7 cents shSampleModes 1 shKeyExclusiveClass 0 This bolded elemets of this vector is what is being emphasized with this preset. The values mean that the sample should be played with an LFO which can modulate Filter Cutoff. The frequency of the LFO should be about 3 Hz. At note-on, the Filter Cutoff should be at about 1 kHz, then DECREASE down to one octave below the initial filter cutoff amount (to 500 Hz), then increase up to 1 octave ABOVE the initial filter cutoff amount (to 2 kHz). Note from the italicized shSampleModes parameter listing that the sample should be played from the start point into the loop points then stay in the loop throughout the release portion. The samples from the loop end to the sample end are never heard. Preset Index : This preset has the same form as Preset Index 0. Upon running the SoundFont Enabler provided, and giving a keynumber of 60 and a velocity of 100, the output will be a single vector with the following parameters: --------- Vector # 0 -------------- dwStart 4888 samples dwEnd 7025 samples dwStartLoop 6883 samples dwEndloop 7017 samples dwSampleRate 44100 Hz OriginalKey 64 Correction 60 cents shModLfoToPitch 0 cents/max excursion shVibLfoToPitch 0 cents/max excursion shModEnvToPitch 0 cents/max excursion shInitialFilterFc 5192 cents above 8.176 Hz shInitialFilterQ 126 cB above DC level shModLfoToFilterFc 0 cents/max excursion shModEnvToFilterFc 3600 cents/max excursion shModLfoToVolume 0 cB/max excursion shChorusEffectsSend 0 units of 0.1% shReverbEffectsSend 0 units of 0.1% shPanEffectsSend 0 units of 0.1% right shDelayModLfo -12000 TimeCents above 1 second shFreqModLfo -1925 cents above 8.176 Hz shDelayVibLfo -12000 TimeCents above 1 second shFreqVibLfo -725 cents above 8.176 Hz shDelayModEnv -12000 TimeCents above 1 second shAttackModEnv -4021 TimeCents above 1 second shHoldModEnv -12000 TimeCents above 1 second shDecayModEnv 1094 TimeCents above 1 second shSustainModEnv 1000 cB Attenuation shReleaseModEnv -12000 TimeCents above 1 second shAutoHoldModEnv 0 TimeCents / KeyNum shAutoDecayModEnv 0 TimeCents / KeyNum shDelayVolEnv -12000 TimeCents above 1 second shAttackVolEnv -12000 TimeCents above 1 second shHoldVolEnv -12000 TimeCents above 1 second shDecayVolEnv -12000 TimeCents above 1 second shSustainVolEnv 0 cB Attenuation shReleaseVolEnv -12000 TimeCents above 1 second shAutoHoldVolEnv 0 TimeCents / KeyNum shAutoDecayVolEnv 0 TimeCents / KeyNum shKeynum 60 shVelocity 100 shInstVol 82 cB of Attenuation shCoarseTune 0 Semitones shFineTune 7 cents shSampleModes 1 shKeyExclusiveClass 0 Looks very much like Preset Index 0, except the parameter emphasized here is the Envelope to Filter Cutoff. That Envelope should have a 1/10 second attack, a 0 second hold (-12000 TimeCents), and a 2 second decay to a fully attenuated sustain level. The Modulation Envelope to Filter Cutoff amount is +3600 cents, the Initial Filter Cutoff is around 171 Hz. This means the filter cutoff should start at 171 Hz, increase to about 520 Hz, then back down to 171 Hz. Also note from the Sample Loop Points that it uses the SAME SAMPLE as Preset Index 0. The bank itself uses a DIFFERENT INSTRUMENT pointing to that sample. This is what allows the difference in articulation data you see above. So looking at Preset Index 0 AND Preset Index 1:  Note that other instruments used by other presets also point to this sample. Preset Index : Similar to Preset Index 2. In this preset, both the Volume and Modulation Envelopes are active upon the sound. The Volume Envelope has an increased attack time. So you should get the same sounds as Preset Index 1 except the volume of the sound should gradually increase to the Initial Attenuation level. Preset Index : Similar to Preset Index 3. In this preset, the Modulation Envelope to Pitch is emphasized. That Envelope has an instantaneous attack, a 1/2 second hold time, and a 1 second decay time. The Modulation Amount is +1200 cents/full excursion. So the note should play immediately at one octave higher than the keynumber and sample data indicates, hold that pitch for 1/2 second, then gradually decay down to the correct pitch. Preset Index :Modulation Similar again. This time the Modulation LFO is set to a frequency of 1 Hz with a pitch modulation amount of +1200 cents/full excursion. So the pitch should start at the original pitch then INCREASE up to one octave higher than the original pitch, then back down to one octave lower than the original pitch. Preset Index :Vib Similar to Preset Index 4, except the Vibrato LFO is used, the frequency is higher, and the amount is -1200 thus the LFO causes the pitch to first decrease then increase. Preset Index : With this preset, we are no longer using the SawBassC3 sample. The preset has the same form as the above presets. The generators produced indicate a 7% reverb amount, and an active Filter AND Volume Envelope Modulation. The Filter modulation happens using the Modulation Envelope. That envelope has an instantaneous attack, full sustain level, and a 28 second release time. That envelope modulates the filter by about 3800 cents/full excursion. The Volume Modulation happens using the Volume Envelope. That envelope has a ~0.8 second attack, full sustain level, and a 2.2 second release time. Preset Index : This is the same as Preset Index 7 with an additional instrument parameter to give the sound a 100% chorus amount. Preset Index : This is similar to Preset Index 6, except the Modulation Envelope is used to modulate both pitch AND filter. The pitch amount is negative, whereas the filter modulation amount is positive. Preset Index : This preset introduces the concept of keyboard SPLITS. The Preset has the following form:  Note the generator list in the instrument now contains key ranges. The lack of key ranges in the previous banks causes the key ranges to default to full key range. The split generators indicate the differences. The First key split has a Volume Envelope attack time of about 1 second. The Second key split that parameter at 0.8 seconds. The third has that parameter at 0.5 seconds. Preset 10: 5 This preset has the following form:  This bank also uses multiple key splits. However some of those splits OVERLAP other splits. This is accomplished with the existence of TWO key splits with the SAME range. This preset also takes advantage of using DIFFERENT samples BETWEEN the key splits. In this preset, each key range resolves into the same sample twice, with one of those key ranges adding a +5 semitone coarse tuning parameter. Thus the 5th saw wave sound. 1617 18 Test Scale Tuning}X}X}Xd WORD}X }X0!IWas20"@;?0~SϠa90"IM0 FW ,Times .+ 4 (+ Preset * Name: LFO Filt * Instrument *  +*QLayer Info Split Info Sample Info () Instrument * Name: LFO Filt * Generators * Sample (1Sample * Name: SawBassC3 gggd WORDg  g,Times .+QLayer Info Split Info Sample Info0Uaxi0"D,0wȡ (  (Preset * Name: Env Filt * Instrument *  ({ Instrument * Name: Env Filt * Generators * Sample dWq0"E` 0U{f (f Sample * Name: SawBassC3 "Y0DUai.0"8B.0wOȡ (+  (" Preset * Name: LFO Filt * Instrument *  (#| Instrument * Name: LFO Filt * Generators * Sample ^^^d WORD^ ^akj0"~6;an0"W>!0m] ,Times .+ QLayer Info Split Info Sample Info (|Sample * Name: SynthDrum as0"~@az0"> (E 0!yʡ (.{ Instrument * Name: Synth Drum * Key Range: 0-55 * Generators * Sample * Key Range: 56-89 * Generators * Sample * KeyRange: 90-108 * Generators * Sample 0bX (u  (jPreset * Name: Synth Drum * Instrument *  dddd WORDd dakj0"~6;aF`0"W> ,Times .(QLayer Info Split Info Sample Infoal0"}Ma0"B (E 0!y ʡ (*z Instrument * Name: Fifth Saw * Key Range: 0-55 * Generators * Sample * Key Range: 56-89 * Generators * Sample * KeyRange: 90-108 * Generators * Sample * . * Key Range: 0-55 * Generators * Sample * Key Range: 56-89 * Generators * Sample * . *  0bX (u  (jPreset * Name: Fifth Saw * Instrument *  aNh0"A0Ag_ (PSample * Name: SawWave1 0n` +!Sample * Name: SawWave2 0c +Sample * Name: SawWave3 as0"Achorded toothPreset Index : Preset Index : In this preset, the instrument has multiple key splits pointing to the same sample. The scale tuning parameter is set differently in each of the key splits. From key 12 to 47, the scale tuning parameter is 0. This means that the pitch value by 0 semitones from the original sample pitch as you increase the keynumber. So basically, every ef,1EcdvGHUVqrFG$%YZ]^de,-RSYsDGR]gms @_CQlyzNOP]^'2345XZxy35QRst56cdxDESXl #(6  @ [12`a \^_8IW-9U]kqCDGQVcy~  (1Qw/8:Z&ƒ›¿  H, \%'5רåæç\cmvāĎĘğĠġĦ5@KU}ƊƋƌƔIJWXYLjdzǴǷ2<[345<>?Aghij!?Hik|ʸ2րֈ @ HҼ H  X:sIx7YuRSOP_45YZ5XHx)Y3l 7p@yEt&B^_./CEFG!! ! ! ! ! ! Vרç }ƌIJYdzǶǷ45Bfgijjk֭֜֝֞JK+,)*>?IJYN! !!!! ! ! ! ! ! Okey sounds like the original sample pitch. From key 48 to 71, the scale tuning parameter is 50. This means that the pitch value increases by 50 semitones from the original sample pitch as you increase the keynumber. This gives a quarter toned key range. From key 72 to 83, the scale tuning parameter is 100, thus a half toned key range. This is the default and typical value for scale tuning. From key 84 to 127, the scale tuning parameter is 200, thus a full toned key range. Each keynumber increases the pitch by a full tone instead of a semitone. As a final note, scale tuning is based on deviations from the ORIGINAL sample pitch AT the ORIGINAL key. An instrument which uses multiple samples layed out to work as a half toned piano WILL NOT BECOME a quater toned piano by simply changing all of the scale tunings to 50! The structure for all of the banks in BASIC.SF2 was the one used with SoundFont version 1, where layer level of the SoundFont bank was not used for any other purpose than to point to an instrument. That bank in its entirety (with the exception of Preset Index 17: Test Scale Tuning) was created with Vienna then converted to SoundFont 2.0. When a SoundFont bank created with Vienna is converted into SoundFont 2.0 format, it changes from a collection of Presets into a collection of Instruments. Then with layer level editing, the developer may quickly and easily create variations or combinations of those instruments in their entirety. Preset Index : In BASIC.SF2, we saw a 5th saw wave created by duplicating all of the splits and adding additional coarse tune generators to each of the copies. A Piano instrument typically contains many key splits (sometimes as many as 10-30 splits). To create a chorded piano, all of those splits would need to be duplicated and tweaked. So to create a Major 7th chorded piano (where each key plays 4 samples) within an instrument would be a nightmare. Here we demonstrate a much easier way of accomplishing the same task using Layering.  The only generators at the LAYER level here are coarse tune adjustments, one for +4 semitones, another for +7 semitones, and a third for +11 semitones. To turn this into a minor seventh, change the coarse tuning parameter in the layer with +4 semitone to +3 semitones. In this preset, a single keynumber and velocity results in 4 output SoundFont vectors. Preset Index : This preset simply shows the notion of a multi-layered preset where the multi-layering occurs at the SoundFont Layer level rather than at the SoundFont split level. It's format is:  TdFd6dd WORDd da0gJ0">2aF`0"j= ,Times .(QLayer Info Split Info Sample Infoaj 0"La 0"J (E 09wȡ (C{ Instrument * Name: Piano * Key Range: 0-x * Generators * Sample * Key Range: x-y * Generators * Sample * KeyRange: y-z * Generators * Sample * . * . * . *  *  *  0Ag_ (PSample * Name: Piano C2 0n` +!Sample * Name: Piano C4 0c +Sample * Name: Piano C6 d WORD1] 1]02\ ZtZt(n (> Preset * Name: Major 7th * Key Range: all * Instrument * Key Range: all * Generators * Instrument * Key Range: all * Generators * Instrument * Key Range: all * Generators * Instrument *  *  *  dWORD da8eR0"C-aHcb}0"K$md_dOdd WORDd da0gJ0">2aF`0"j= ,Times .(QLayer Info Split Info Sample Infoaj 0"La 0"J0Ag_ (PSample * Name: Piano C2 0n` +!Sample * Name: Piano C4 0c +Sample * Name: Piano C6 a8eR0"C-aHdb~0"K$d WORD`] `]0a\ VXVX( (k Preset * Name: Organ 1 * Key Range: all * Instrument * Key Range: all * Instrument *  *  *  dWORDd WORD8vɡ  8vHSHS(C{ Instrument * Name: Piano * Key Range: all * Generators * Sample *  *  *  (E 09wȡdWORDd WORDvɡ  v({ Instrument * Name: Piano * Key Range: all * Generators * Sample *  *  *  ( 0wȡdWORD using the techniques demonstrated abovewelcome datacompThis demonstrates the simplest thing you can do with layering.  Two different organs are layered to give a more full organ effect. No Layer generators are used for tweaking of the data. Preset Index : #$QRfֈ֍ֽ֛֚֝֞֫֬.9HIדןu=JTd<=6X%Nj|bu/=>\^ JWXY H @  [ LMNgn%R!Ddfwx $%.1:?LqtFKL H` H  H @  HhSThis preset uses the same concept as Preset Index 1, except there are keyrange fields at the Layer level. With this preset, you have a fretless bass from keynumbers 0 to 47 and you have a piano from keynumbers 48 to 127. Preset Index : This preset xxxd WORDx xaa{0"nM ,Times .(QLayer Info Split Info Sample Infoa0"N0U{a (dSample * Name: OrganWave1 0w (Sample * Name: Slow Organ Wave a/jI0"G-af0"F(d WORDU b U b0V a EtEt( (`Preset * Name: Organ 1 * Key Range: all * Instrument * Key Range: all * Instrument *  *  *  dWORD  x(Dz Instrument * Name: Organ * Key Range: all * Generators * Sample *  *  *  09wȡ (y Instrument * Name: Slow Organ * Key Range: all * Generators * Sample *  *  *  ( 0wIndex 11 Preset Index 12: Global Tremelo This preset introduces the concept of the GLOBAL SPLIT. A global split of generators contains generators which apply to ALL splits UNLESS they are RE-DEFINED in the individual split. The bank has this format:  The fact that a group of generators appears BEFORE the first keyrange tells you that those generators in the global split. In this bank, there are two generators in the global split, one indicating a Modulation LFO frequency of 1 Hz, the other indicating a Modulation LFO to Volume amount of +120 cB/max excursion. However the second split has a Modulation LFO frequency of 2 Hz. That is why they are both moving at different frequencies. The advantage of Global Splits is not demonstrated here, since only a limited number of generators are used. However when many generator values are the same in a group of splits, the advantage of the Global Split becomes evident. Preset Index 13: Velocity Splits Velocity can also specify a split in SoundFont bank. This preset has the following form:  In this bank, a velocity value of 0 through 69 triggers the Saw Bass Wave. A velocity value of 90 through 127 triggers the String Sample wave. A velocity value of 70 through 89 triggers both samples. Each split has its own set of additional generators. Preset Indices 11, 12, and 13uses both velocity and keynumber splits to define layers. It also has layer generators to add -36 semitones to the pitch of the Organ1 instrument. Preset Index 4: Layering Violins This preset introduces the concept of the Global Layer. A Global Layer of generators contains generators which apply to ALL layers UNLESS they are RE-DEFINED in the individual layer. Those layer parameters are then added to the instrument generators in the final output vector. In this preset, the Global Layer contains a single generator to add 10% reverb to all layers. There are then 3 layers of the 'strings' instrument. Those layers have benign parameters with the exception of the attack time parameter. 1000% (12000 TimeCents) attack time is added to the first layer, defined at keynumbers 0 to 39. 500% (6000 TimeCents) attack time is added to the second layer, defined at keynumbers 40 to 79. Finally, -200% (-2400 TimeCents) attack time is added to the third layer, defined at keynumbers 80 to 127. Preset Index 5: Layered Weirdness This preset is a mixed bag of some of the various layering functions demonstrated with the previous presets. Here we have a preset with three layers, each pointing to a single instrument. That instrument has two splits, each pointing to a single sample. A way of looking at this preset is as follows:  This bank uses many of the techniques described in BASIC.SF2 and some instrument layering similar to the Organ1 Preset of LAYERING.SF2.!YYYd WORDY Yakj0"~6;aXr0"eG0X ,Times .+ QLayer Info Split Info Sample Info (Sample * Name: StringSamp a|0"F (E 0!yʡ (+z Instrument * Name: Inst w/ Glob * Generators * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample 0bX (u  (jPreset * Name: Global Trem * Instrument *  0OuW (]Sample * Name: SawBassC3 YYYd WORDY Yakj0"~6;aXr0"eG0X ,Times .+ QLayer Info Split Info Sample Info (Sample * Name: StringSamp a0"F (E 0!y (,~ Instrument * Name: Velocity Spl * Key Range: all * Vel Range: 0 to 90 * Generators * Sample * Key Range: all * Vel Range: 70 to 127 * Generators * Sample 0bX (u  (jPreset * Name: Velocity Spl * Instrument *  0OuW (]Sample * Name: SawBassC3   d WORD 0}0Yv0/ T"SX"u"S+ܡ ,Times .+SawBassC3 Sample (gYStrings Inst: Split 1: 1/2 sec hold time Strings Inst: Split 2: 1 sec hold time (< Layer 1: * Add VibLFO (=!Layer 2: Add attack and hold time (94 Layer 3: * Decrease Fc, * Increase Env to Fcd WORDWd WORDHd WORDJ -A4.8!844;8dWORDd WORDJ $84%/!84+28dWORDd WORDI}d WORDY  /4&!84")8dWORD 04!8dWORDdWORDd WORDId WORDY  4 !848dWORD 4!8dWORDdWORDd WORDed WORDHd WORDJ 4!848dWORDd WORDJ 4!848dWORDd WORDI}d WORDY  4!848dWORD 4!8dWORDdWORDd WORDId WORDY  4!848dWORD 4!8dWORDdWORDd WORD&d WORDHd WORDJ 4!848dWORDd WORDJ 4!848dWORDd WORDI}d WORDY  4!848dWORD 4!8dWORDdWORDd WORDId WORDY  4!848dWORD y4z!8dWORDdWORDd WORDd WORDJ p4q{!84w~8dWORDd WORDJ g{4hr!84nu8dWORDd WORDI}d WORDY  ^r4_i!84el8dWORD Us4V`!8dWORDdWORDd WORD L`4MW!84SZ8dWORDd WORDd WORDY  CW4DN!84JQ8dWORD :X4;E!8dWORDd WORDvd WORDJ 1E42<!848?8dWORDd WORDJ (<4)3!84/68dWORDd WORDI}d WORDY  34 *!84&-8dWORD 44!!8dWORDdWORDd WORDn !4!848dWORD "" " " " " 484848d WORD6W 6W07B0AL0KVdWORDd WORDTu Tu0U`0_j0itdWORD 0s~0}4GN84QX84fm84pw84z8 (TH40 60 80 (<Layer +)Split + Sample (KeynumLAYERINGthe layeringLAYERINGa ROM ONLY bank which REQUIRES the 1MGM ROM to play back correctly, so it should serve to test ROM detection capabilites of the prototype synthesizer as wellthat bank is convertedSYNTHMIDI127 and the standard drum kit. SYNTHGMa ROM ONLY bank which REQUIRES the 1MGM ROM to play back correctly, so it should serve to test ROM detection capabilites of the prototype synthesizer as wellEITHER   ^ _ d v     ` x ~             W \dpx+04Z$$G$Q$[$$$$$$$$$$$$$$%%%8%L&& &;&W&X&Z&\&b&f&h&|&}&&&&&&&&&&'  @ @  H@  T\]CDefst           $$%%&Y&Z&g&h(1(2(P(k(l)))0)1)),,,----........//0`!! ! !! ! !! ! ! ! ! M OR MIDI Bank index 128. The presets under MIDI Bank index 128 are the percussive presets. That is where the standard drum kit exists. These sounds should be played with Note-on events on MIDI channel 10 of a GM compatable wavetable synthesizer compatable MIDI Some presets in this bank use the notion of the overriding keynumber. Note that when one is encountered, it changes the inputted MIDI key number into the value obtained there. Also note that the same effect would be created by setting the scale tuning in a split to 0 and perhaps adjusting the coarse tuning to obtain the proper pitch. GMGSMT2M.SF2 This is the standard 2MB General MIDI/GS/MT-32 SoundFont bank. GMThe MIDI bank/preset indices correspond to a General MIDI synthesizer with GS variations at the various bank indices and an MT-32 synthesizer located at MIDI BANK index 127. It also contains a percussive bank at MIDI bank index 128 with the Standard Drum kit living at Preset 0 and variations living at other preset index numbers. The MT-32 drumkit lives at bank index 128 preset index 127. , GS, or MT-32compatable MIDI welcome datacompSTEREO.SF2 SoundFont stereo implementationSTEREOcontains all the samples necessary to play the soundsthe stereo implementation of a SoundFont compatable wavetable synthesizer. Preset Index 0: 16 Bit 44kHz This preset uses the sample 16 bit 44 kHz stereo waveform which shipped with the AWE-32 (s_16_44.wav). This preset has the following form:  '''+','8'H'L'W'X(0(2(@(K(O(P(`(j(l(v((())/)=)M)j))))),,-E-L-R-Y---.v.y......../////00(0_0a0o0}001K1Y1[1j1111111242D2I2Q H:P H8 H5@ @ H2l    N The samples pointing at each other indicate two distinct facts: 1. That the sample in question clains it is a Right sample, a Left sample, or a Linked sample, and 2. That the sample they point to is contained within the closure of the splits. These samples should be played with the pitch phase locked and the RIGHT sample as the pitch master. The pan positions SHOULD NOT BE ASSUMED! The values are contained within the generator splits! Preset Index 1: 8 Bit 22kHz This preset has the same archetecture as Preset Index 0, except a different stereo pair of samples is used. This one is the s_8_22.wav file which shipped with the AWE-32. Preset Index 2: Not Stereo 1 This preset has the following form:  The samples pointing into outer space indicate two distinct facts: 1. That the sample in question clains it is a Right sample, a Left sample, or a Linked sample, and 2. That the sample they point to is NOT contained within the closure of the splits. Even though there is a left and a right sample, they are not each other's LINKS. This preset should not be played back with the samples pitch phase locked. Preset Index 3: Not Stereo 2 This preset has the following form:  Because only ONE of the sample pair was in the instrument closure, this sample was intended to be played as a MONO sample, not as a stereo sample with its partner. Preset Index 4: Stereo -n- Mono This preset has the following form:  Note that this preset layers the s_16_44 stereo instruments with the NotStereo2 instrument used above. This should be played with the stereo samples pitch phase locked and the third sample played as a mono sample.eeed WORDe  e,Times .+ QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_16_44 [L] ds,0"@J10 Rb (R ( Preset * Name:16Bit 44k * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: s_16_44 * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample *  *  *  0dF>`X0";Kd-JGd0"SWd '0"SGdQk0"tKd WORD  ,Times .+ QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_8_22 [L] ds,0"@J10 Ob (R ( Preset * Name:NotSt1 * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: NotSt1 * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample *  *  *  0dr-" vd,eF"SWd '0"SGdQk0"tK>0 d WORD  ,Times .+QLayer Info Split Info Sample Info0;b ($Sample * Name: s_8_22 [R] ds,0"@J10 Pb (R ( Preset * Name:NotSt2 * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: NotSt2 * Key Range: all * Generators * Sample *  *  *  0]dr-" vd '0"SGtttd WORDt  t,Times .+QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_16_44 [L] ds,0"@J10 oa (R ( Preset * Name:16Bit 44k * Key Range: all * Instrument * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: s_16_44 * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample *  *  *  0dF>`X0";Kd-JGd0"SWd '0"SGdQk0"tKao$0"Y>:=0` +Sample * Name: s_8_22 [R] 0d0"Gʠa~f0"b ( Instrument * Name: NotSt2 * Key Range: all * Generators * Sample stereo welcome datacomp SoundFont Compatability The SoundFont Compatability Validation Sis simply a collection ofwhich are designed to beBanksBASIC, and sound sets (i.e. MIDI SoundFont banks such as GM.SF2) are providedeither containor reference the 1MGM The provided SoundFont Enabler software described above with the provided SoundFont Enabler Driver interface software will produce correct vectors of articulation data for all of these banks2Q2i>.>/>5>F>G>_>`>>>>>>>>>>>? ??.?4?=?O?X?h????DD D&DCDHDSDbD{D~DDDDE E"E:E^EfEgElEEEEEEFIIIIIJJJJJJJJJ%J&J'J6J7J9J:J;J<J>J?J@JAJBJDJEJFJG HF  $$ @ Y0`0a0~00001J1K1k1l1111?/?0E!E"JLJsWjWWWZX[[[d[q[[[[[]h]]]]]]^)^:^I^^^l^x^_ _$_%_I_K` ` aaa'a(a9a:xy}y~yyzz z,zTz{zzz{׿ſ! ! ! !! ! ! !! ! !! K and save those vectors to files. That software should be compiled as is to produce output vectors with this suite of banks. These vectors should be compared with the vectors or programming instructions generated by the developer's SoundFont compatable wavetable synthesizer for accuracy. dFont() compatibility. or reference samples located on the Emu 8011 1MGM wavetable ROM which is used on the AWE-32.some of across the keyboard instrumentGlobal Splits are most beneficial when a preset contains many splits which all would require the same articulation data. That articulation data would appear only once. Fuck you, what the hell do you want?gggd WORDg  g,Times .+ QLayer Info Split Info Sample Info0Uaxh0"D+0wȡ (  (Preset * Name: Env Filt * Instrument *  ({ Instrument * Name: Env Filt * Generators * Sample dWq0"E` 0U{f (f Sample * Name: SawBassC3 "Y0DUah.0"8B-0wOȡ (+  (" Preset * Name: LFO Filt * Instrument *  (#| Instrument * Name: LFO Filt * Generators * Sample "hX *{other instruments}"  on data would appear only once within the instrument, as opposed to once per split.presetsprocessedMIDI implementation. D FindCk(): F ReadCkHdr(): G G Aa specified amount ofRAM The banks may NOT be distributed or used for other purposes without written consent. BASIC.SF2required sample dataTcomplexHere is a rough diagram of the preset:When the SoundFont Enablernavigates this preset withnare the emphesis of this preset. The values indicatemodulatingFilter Cutoff should be1 kHz, then DECREASE down(to 500 Hz), then increase upWhen the SoundFont EnablernavigatesJGJHJIJKJPJQJRJSJTJUJVJkJnJoJpJqJrJvJwJxJJJJJKKK3K<KIKMKNKKKKKKKNN NNN'N*N1N2N3NINQNSNgNpNxNyNNNNNNNNNOOO,OgOOOOOOOOPPPP P'P5PcPoPwPxPPP  HS @ HQ X this preset withThe vector lModulation0.1minimalLThe output vector is saffect tThthen gradually decayindicatedursion. The original pitch then INCREASEmore than one key range, tEatThat is why they areLAYEREDLAYEREDThe structure for allAll presets in that bank Creative Technologies SoundFont Studio using Creative Technologies SoundFont 1.0 to 2.0 Convertera SoundFont 2.0 Editor such as E-mu Systems Preditor using BASIC.SF2 contains"" preset AYER leveluse ofThis preset shows the notion of multi-layereing aoccuringTcan be viewed GM compaticompatithis generatoruntered, it changes the input MIDI key number generator's GMGSMT2M implementation,. The bank is splitwithin mentation of a SoundFont compatiTTned within the generator splits (TYPICALLY left panned -500, right panned +500) and SHOULD NOT BE ASSUMED.The sample in question claimT In other words, this preset plays two independent mono samples which happen to be elements of two different stereo pairs.Z }X}X}Xd WORD}X }X0!IWas20"@;?0~bϠa30"S=֠0 FW ,Times .+ 4 (+ Preset * Name: LFO Filt * Instrument *  +*QLayer Info Split Info Sample Info () Instrument * Name: LFO Filt * Key Range: all * Generators * Sample (1Sample * Name: SawBassC3 5g'ggd WORDg  g,Times .+ QLayer Info Split Info Sample Info0Uaxf0"D*0wȡ (  (Preset * Name: Env Filt * Instrument *  ({ Instrument * Name: Env Filt * Key Range: all * Generators * Sample dWq0"RQ0U{f (f Sample * Name: SawBassC3 "Y0DUag.0"8B-0wZȡ (+  (" Preset * Name: LFO Filt * Instrument *  (#| Instrument * Name: LFO Filt * Key Range: all * Generators * Sample "ej (x{other instruments}" contains all of the samples necessary . 01e same concept as Preset Index 0instrument instrument 2o be duplicated and tweaked. Using that technique, creatingLayersPreset Index 4: LayeredMixed Bag SYNTHGMS.SF2 This bank is the same as SYNTH !PPPPPQQ.Q/QJQKQW W/W1W2W3WSW^WiWjWkWWWWWWWWWZZZ#Z6ZIZNZVZXZYZZZZZZZZZZ[[[[[\k\\]g]h]|]]]]]]^^9^;^]^_^m^w^^^^^^^^_ Hf| @ Hb    @ QGM.SF2 except the file contains all of the sample data necessary to play back the bank. It uses samples originally contained in the The samples within the bank are now 16 bit samples with the bottom bytes of each sample set to 0. The corresponding within this SoundFont Also note that pitch calculation occurs with a combination of the following fields: dwSampleRate shOrigKeyAndCorr shCoarseTune shFineTune shScaleTune Finally, some presets in this bank use the keyExclusiveClass field. This is used mostly in the drum banks. Keynumbers 42, 44, and 46 map out to be different hi-hat positions. The output vectors will show that they have matching non-zero keyExclusiveClass mask values. That is the synthesizer's key to seek out any voice playing on that MIDI channel with that same keyExclusiveClass value and silence it immediately BEFORE playing the current note. The instruments in this bank were constructed using 4 samples, namely: s_16_44 [R] s_16_44 [L] s_8_22 [R] s_8_22 [L] The presets in this bank were constructed using 6 instruments, namely: Stereo CarStart Mono CarStart Other Mono CarStart Stereo Noise Mono Noise CarStart and Noise Stereo 1. That WAV file sounds like 2 cars startingStereo 2 s_8_22.wav file That WAV file sounds like an arcade game noise. Preset Index 2: Stereo 3 This preset has the following form:  Note that this preset should ALSO be treated as a stereo preset because the LINKED sampls were found in the Preset Closure. In this instance, that happened with Layers instead of with Splits. Since the Mono CarStart and the Other Mono CarStart instruments are panned center, there are Layer generators which move the pans to full left and full right. Again, the generator values are controlling this action, there is no assumed pan position. Preset Index 3Preset Index 4 Preset Index 5 2 This preset is similar to Preset Index 4, except the mono instrument chosen was the Mono CarStart, not the Mono Noise. Since the stereo instrument is the Stereo CarStart, the result of the initial navigation would tell you that there are TWO samples in the Preset Closure which are pointing to the Left Sample. Only one of them may do so in a stereo pair, the second should be treated as a mono sample.džxxxd WORDx xaa{0"nM ,Times .(QLayer Info Split Info Sample Infoa0"N0U{a (dSample * Name: OrganWave1 0w (Sample * Name: Slow Organ Wave a/jI0"G-af0"F(d WORDU b U b0V a oVoV( (`Preset * Name: Organ 1 * Key Range: all * Instrument * Key Range: all * Instrument *  *  *  dWORD  x(Dz Instrument * Name: Organ * Key Range: all * Generators * Sample *  *  *  09wȡ (y Instrument * Name: Slow Organ * Key Range: all * Generators * Sample *  *  *  ( 0w!eeed WORDe  e,Times .+ QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_16_44 [L] ds,0"@J10 Rb (R ( Preset * Name: Stereo 1 * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: Stereo Car * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample *  *  *  0dF>`X0";Kd-JGd0"SWd '0"SGdQk0"tK xxwxd WORDx xaa{0"nM ,Times .(QLayer Info Split Info Sample Infoa0"N0U{a (dSample * Name: s_16_44 [R] 0w (Sample * Name: s_16_44 [L] a/jI0"G-af0"F( (Dz Instrument * Name: Mono Car * Key Range: all * Generators * Sample *  *  *  09wȡ (y Instrument * Name: Other Car * Key Range: all * Generators * Sample *  *  *  ( 0w0V a ( (`Preset * Name: Stereo 3 * Key Range: all * Instrument * Key Range: all * Instrument *  *  *  a<V0"{I8aoMg0"ZȠ d WORD  ,Times .+ QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_8_22 [L] ds,0"@J10 Ob (R ( Preset * Name:NotSt1 * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: Car&Noise * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample *  *  *  0dr-" vd,eF"SWd '0"SGdQk0"tKG?1!d WORD  ,Times .+QLayer Info Split Info Sample Info0;b ($Sample * Name: s_8_22 [R] ds,0"@J10 Pb (R ( Preset * Name:NotSt2 * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: MonoCar * Key Range: all * Generators * Sample *  *  *  0]dr-" vd '0"SGtttd WORDt  t,Times .+QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_16_44 [L] ds,0"@J10 oa (R ( Preset * Name:16Bit 44k * Key Range: all * Instrument * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: Stereo Car * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample *  *  *  0dF>`X0";Kd-JGd0"SWd '0"SGdQk0"tKao$0"Y>:=0` +Sample * Name: s_8_22 [R] 0d0"Gʠa~f0"b ( Instrument * Name: MonoNoise * Key Range: all * Generators * SampleSppppppp_ _ _$_%_H_I_J_____` ``#`?`b``````aaaaa$a%a&a(a6a8awaaaaaab$bRbbbuuxx,x.x^xmxxxxxxxxxyy%y'y7yhyry|y}y{{_|{~~  @ Hr Ho Hl HiT  P127 and the standard drum kit. SYNTHGMS.SF2 . In the Standard Drum Kit, (bank 128, preset 0) kfor those keys values. That tells the immediately silence that voiceSYNTHGMSes of the prototype synthesizer he sample in question claim(in the shSampleTypes field of the Sample Header data structure) (in the shSampleTypes field of the Sample Header data structure) (in the sf(in the sf Navigating this preset with keynumber 60 and velocity 100 produces the following two output vectors: --------- Vector # 0 -------------- dwStart 0 samples dwEnd 137025 samples dwStartLoop 91350 samples dwEndloop 137018 samples dwSampleRate 44100 Hz OriginalKey 0 Correction 60 cents shModLfoToPitch 0 cents/max excursion shVibLfoToPitch 0 cents/max excursion shModEnvToPitch 0 cents/max excursion shInitialFilterFc 13500 cents above 8.176 Hz shInitialFilterQ 0 cB above DC level shModLfoToFilterFc 0 cents/max excursion shModEnvToFilterFc 0 cents/max excursion shModLfoToVolume 0 cB/max excursion shChorusEffectsSend 0 units of 0.1% shReverbEffectsSend 0 units of 0.1% shPanEffectsSend -500 units of 0.1% max excursion shDelayModLfo -12000 TimeCents above 1 second shFreqModLfo 0 cents above 8.176 Hz shDelayVibLfo -12000 TimeCents above 1 second shFreqVibLfo 0 cents above 8.176 Hz shDelayModEnv -12000 TimeCents above 1 second shAttackModEnv -12000 TimeCents above 1 second shHoldModEnv -12000 TimeCents above 1 second shDecayModEnv -12000 TimeCents above 1 second shSustainModEnv 0 cB Attenuation shReleaseModEnv -12000 TimeCents above 1 second shAutoHoldModEnv 0 TimeCents / KeyNum shAutoDecayModEnv 0 TimeCents / KeyNum shDelayVolEnv -12000 TimeCents above 1 second shAttackVolEnv -12000 TimeCents above 1 second shHoldVolEnv 4000 TimeCents above 1 second shDecayVolEnv -12000 TimeCents above 1 second shSustainVolEnv 0 cB Attenuation shReleaseVolEnv -12000 TimeCents above 1 second shAutoHoldVolEnv 0 TimeCents / KeyNum shAutoDecayVolEnv 0 TimeCents / KeyNum shKeynum 60 shVelocity 100 shInstVol 0 cB of Attenuation shCoarseTune 0 Semitones shFineTune 0 cents shSampleModes 12 shKeyExclusiveClass 0 --------- Vector # 1 -------------- dwStart 137071 samples dwEnd 274096 samples dwStartLoop 228421 samples dwEndloop 274089 samples dwSampleRate 44100 Hz OriginalKey 0 Correction 60 cents shModLfoToPitch 0 cents/max excursion shVibLfoToPitch 0 cents/max excursion shModEnvToPitch 0 cents/max excursion shInitialFilterFc 13500 cents above 8.176 Hz shInitialFilterQ 0 cB above DC level shModLfoToFilterFc 0 cents/max excursion shModEnvToFilterFc 0 cents/max excursion shModLfoToVolume 0 cB/max excursion shChorusEffectsSend 0 units of 0.1% shReverbEffectsSend 0 units of 0.1% shPanEffectsSend 500 units of 0.1% max excursion shDelayModLfo -12000 TimeCents above 1 second shFreqModLfo 0 cents above 8.176 Hz shDelayVibLfo -12000 TimeCents above 1 second shFreqVibLfo 0 cents above 8.176 Hz shDelayModEnv -12000 TimeCents above 1 second shAttackModEnv -12000 TimeCents above 1 second shHoldModEnv -12000 TimeCents above 1 second shDecayModEnv -12000 TimeCents above 1 second shSustainModEnv 0 cB Attenuation shReleaseModEnv -12000 TimeCents above 1 second shAutoHoldModEnv 0 TimeCents / KeyNum shAutoDecayModEnv 0 TimeCents / KeyNum shDelayVolEnv -12000 TimeCents above 1 second shAttackVolEnv -12000 TimeCents above 1 second shHoldVolEnv 4000 TimeCents above 1 second shDecayVolEnv -12000 TimeCents above 1 second shSustainVolEnv 0 cB Attenuation shReleaseVolEnv -12000 TimeCents above 1 second shAutoHoldVolEnv 0 TimeCents / KeyNum shAutoDecayVolEnv 0 TimeCents / KeyNum shKeynum 60 shVelocity 100 shInstVol 0 cB of Attenuation shCoarseTune 0 Semitones shFineTune 0 cents shSampleModes 4 shKeyExclusiveClass 0 shSampleModes shSampleLinks 1 es shSampleLinks 0Note the vector represents sample link status in the shSampleModes field and the vector link indices in the shSampleLinks field. SampleModes with the 4th bit set says the vector is the head of a group of linked samples. Sample Modes with the 3rd bit set says the vector contains a linked sample! ! ! ! ! ! ! !FG45+-./0123456789:;<=>?@ABCDE[stuvwxyz{|}~*Q[MO[glq~ @ ^{{2{a{{{| |O|{||}}@}p}}~~K~~~ N|%^:X*+S{ +ZHw.g9rGuNGd-\N! ! ! Z. The shSampleLinks field tells which VECTOR the current vector is linked to. % full right% full rightfull rightshSampleLink full rightshSampleLink Note the vectorhas its own way of representing sample links within a vector group. The is contained (FIRST_LINK) (LINKED)the index of the (the vector with the 4th or FIRST_LINK bit set) Not Stereo 1NOTpreset because the LINKED samplesnot individual Instruments : Not Stereo 2: Not Stereo 3<  7@A45?@NObcefef45JKnomBu! !  !2! ! ! !! ! !Q]^aab`acdff-25:>`:CRg 00 H H @ H  QaxSxCxd WORDx xaa{0"nM ,Times .(QLayer Info Split Info Sample Infoa0"N0U{a (dSample * Name: s_16_44 [R] 0w (Sample * Name: s_16_44 [L] a/jI0"G-af0"F( (Dz Instrument * Name: Mono Car * Key Range: all * Generators * Sample *  *  *  09wȡ (y Instrument * Name: Other Car * Key Range: all * Generators * Sample *  *  *  ( 0w0V a ( (`Preset * Name: Stereo 3 * Key Range: all * Instrument * Key Range: all * Instrument *  *  *  This is a pair of INSTRUMENTS each with ONE MONO Sample, where the samples happen to be LINKED to each other within a DIFFERENT INSTRUMENT than is used here. Preset Style To Be Avoided: A preset of this form is considered to have an indeterminate sound:  The reason it is indeterminate is that the PITCH related generators pointing to the RIGHT sample controls the pitch of the stereo pair. But since TWO splits BOTH point to the SAME RIGHT sample, which SPLIT's pitch controls the stereo pair is not defined. The Navigator in the SoundFont enabler will treat the samples in the first and second split as a liked pair and the sample in the third split as a mono sample. However if you change the order of the splits such that what WAS the third split is NOW the first split and vice-versa, the Navigator in the SoundFont enabler may produce vectors corresponding to a different sound. Therefore by changing the ORDER of the splits, you change the NATURE of the sound. The reason this practice is not FORBIDDEN in SoundFonts is because that would limit flexibility. A preset has this structure but WITHOUT key/velocity split overlapping may prove to be beneficial. An example would be if one changed the velocity ranges of the two splits which control the RIGHT sample such that they DON'T overlap. That has a defined sound for all key and velocity ranges, and gives you Velocity to Stereo Sample Pitch control. IE if each split using the RIGHT sample had different vibrato amounts, one would get different pitch vibrato amount on the entire stereo sound when the key is hit softly or hard.eeed WORDe  e,Times .+ QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_16_44 [L] ds,0"@J10 Rb (R ( Preset * Name: Stereo 1 * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: Stereo Car * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample *  *  *  0dF>`X0";Kd-JGd0"SWd '0"SGdQk0"tKe eed WORDe  e,Times .+ QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_16_44 [L] ds,0"@J10 Rb (R ( Preset * Name: Stereo 1 * Key Range: all * Instrument *  *  *  *  ($ Instrument * Name: Stereo Car * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample * Key Range: all * Generators * Sample *  *  *  0dF>`X0";Kd-JGd0"SWd '0"SGdQk0"tKa"<0"6The SoundFontPreset Index 6 Stereo Vel SplitsThis preset takes adatage of the unique qualities associated with pitch control of stereo samples and careful split determination:Note that there are two NON-OVERLAPPING splits pointing to the RIGHT sample. NON-OVERLAPPING means that for all possible key and velocity values only one of those SPLITS will be active. The first split pointing to the RIGHT sample has a coarse tuning of -12 semitones, a Modulation Envelope to Pitch amount of 1200 cents/max excursion and a Modulation Envelope attack time of 5 seconds. The effect is if you hit the key soft, the ENTIRE STEREO SAMPLE (including the LEFT sample) gradually increases in pitch. The second split pointing to the RIGHT sample has a coarse tuning of +12 semitones, a Vibrato LFO to Pitch amount of -1200 cents/max excursion and a Vibrato LFO frequency of 0.5 Hz. The effect is if you hit the key hard, the ENTIRE STEREO SAMPLE gradually decreases in pitch then increases. So effectively, you have velocity to stereo sample pitch control. One may also note that it is quite easy to change the splits from being NON-OVERLAPPING to being OVERLAPPING by increasing the first split's maximum velocity without increasing the second split's minimum velocity. In this case, you have a sound which is NOT defined according to the SoundFont 2.0 specification in the overlapping split range. This should be avoided.?e1e!ed WORDe  e,Times .+ QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_16_44 [L] ds,0"@J10 Rb (R ( Preset * Name: StereoVel * Key Range: all * Instrument *  *  *  *  (" Instrument * Name: Stereo Vel * Key Range: all * Vel Range: 0 to 90 * Generators * Sample * Key Range: all * Generators * Sample * Key Range: all * Vel Range: 91 to 127 * Generators * Sample *  *  *  0dF>`X0";Kd-JGd0"SWd.0"]G &a^x0"=theycreated  Those two splits are distinguished by velocity range. ݻ  9:_`7^Gw 9f+\8q=lOz.Lz67_8h*Z +KLYsyz9[\g ,m -BFPXb _cklABRWXc,GKm 00 H @ H   RAe3e#ed WORDe  e,Times .+ QLayer Info Split Info Sample Info0;b ($Sample * Name: s_16_44 [R] 0Syc (dSample * Name: s_16_44 [L] ds,0"@J10 Rb (R ( Preset * Name: StereoVel * Key Range: all * Instrument *  *  *  *  (" Instrument * Name: Stereo Vel * Key Range: all * Vel Range: 0 to 120 * Generators * Sample * Key Range: all * Generators * Sample * Key Range: all * Vel Range: 121 to 127 * Generators * Sample *  *  *  0dF>`X0";Kd-JGd0"SWd.0"]G &a]w0"< by Mike GuzewiczdFont 1993-1995 Systems, Inc. All Rights Reservedse this bank for the purpose of verification from E-mu Systems, Inc thein the SoundFont Compatability Validation Suite is The SoundFont Enabler software is "Copyright 1994-1995, E-mu Systems, Inc. / Creative Technologies Ltd. All Rights Reserved." License is hereby granted by the copyright holder to copy and use this code for the purposes of developing SoundFont compatible software. This software may NOT be distributed or used for other purposes without written consent from E-mu Systems, Inc. or Creative Technologies, Ltd. This software and information is provided "as is" without warranty of any kind, either expressed or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose. !RcDc4cd WORDc ca f#0"[=2a90"C= ,Times .+QLayer Info Split Info Sample InfoaC]0"kLav0"J ( 0vǡ ({ Instrument * Name: Piano * Key Range: 0-x * Generators * Sample * Key Range: x-y * Generators * Sample * KeyRange: y-z * Generators * Sample * . * . * . *  *  *  0@^ ()Sample * Name: Piano C2 0Gm_ +!Sample * Name: Piano C4 0rb +Sample * Name: Piano C6 d WORD \ \0 [ SxMSxM(G ( Preset * Name: Major 7th * Key Range: all * Instrument * Key Range: all * Generators * Instrument * Key Range: all * Generators * Instrument * Key Range: all * Generators * Instrument *  *  *  dWORD cad+~0"~B-a!c;}0"J$ E 7 'd WORDd WORD   0e |0A ^0 <";U"]";(ܡ ,Times .+uSawBassC3 Sample (OYStrings Inst: Split 1: 1/2 sec hold time Strings Inst: Split 2: 1 sec hold time ($ Layer 1: * Add VibLFO (%!Layer 2: Add attack and hold time (!1 Layer 3: * Decrease Fc, * Increase Env to Fcd WORDWd WORDHd WORDJ |*>4}+5!84}188dWORDd WORDJ |!54}",!84}(/8dWORDd WORDI}d WORDY  |,4}#!84}&8dWORD {-4}!8dWORDdWORDd WORDId WORDY  |4}!84} 8dWORD {4}!8dWORDdWORDd WORDed WORDHd WORDJ |4}!84}8dWORDd WORDJ |4}!84}8dWORDd WORDI}d WORDY  |4}!84}8dWORD {4}!8dWORDdWORDd WORDId WORDY  |4}!84}8dWORD {4}!8dWORDdWORDd WORD&d WORDHd WORDJ |4}!84}8dWORDd WORDJ |4}!84}8dWORDd WORDI}d WORDY  |4}!84}8dWORD {4}!8dWORDdWORDd WORDId WORDY  |4}!84}8dWORD {v4}w!8dWORDdWORDd WORDd WORDJ |m4}nx!84}t{8dWORDd WORDJ |dx4}eo!84}kr8dWORDd WORDI}d WORDY  |[o4}\f!84}bi8dWORD {Rp4}S]!8dWORDdWORDd WORD |I]4}JT!84}PW8dWORDd WORDd WORDY  |@T4}AK!84}GN8dWORD {7U4}8B!8dWORDd WORDvd WORDJ |.B4}/9!84}5<8dWORDd WORDJ |%94}&0!84},38dWORDd WORDI}d WORDY  |04}'!84}#*8dWORD {14}!8dWORDdWORDd WORDn | 4} !84}8dWORD  "}" " "} " "} 4}84}84} 8d WORD6W |3T0}4?0}>I0}HSdWORDd WORDTu |Qr0}R]0}\g0}fqdWORD  0}p{0}z4}DK84}NU84}cj84}mt84}w~8 (QH40 60 80 ($Layer +)Split + Sample (KeynumdWORD ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! !! ! ! ! !! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! uvw @ H Hx  @ 3 .u8'B4L*T`Rm#xn4rɌϱm  :_    ' +"]L19 5HQREFPQkl ~  8 9$DEQR#$ZHBCxy34pq+=O^p:;JK1Qp-011233J3z3344:4~46j99: :?:W>>>T>>>ABkBBBCGJJLLL)L*L:L;LILZLmLLLLLLL\]A]]]^n^^_5rsscsz{$3'fgFGZ[ 134%Ls5e!Nx@yX#R6a3b@Ars9:a8h(X >wQU%^*Yv 'CD#$5789 89 ]_QR+,QR%&OP}~VWqrtu'(rs”•Ńń23ȦȨɉɊɌɍ$%9:2͇͈͊͋͌Δϱϲ ! FHIJLYZmnIJ؞؟ڣڤڰڱۄۅےۓݢݣޜޞީު3@MYef+,QR)Px 9i+XN*c/^Al >l()Qy*ZL|4mAzP X$Sp5QR34PQjk "#QRopab     ./ !2! !! ! ! !! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! !! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! !}! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! !! ! ! ! !! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! !! ! ! ! ! ! ! ! ! !!! ! !!! ! ! ! !!! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! !! ! ! ! ! ! ! ! !! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! ! !! ! ! ! ! ! ! ! ! ! >ueֈ'2QJGP_ GHQlmno|};Tv0`{NuIJKLMNpq  x HH(FG(HH(d @=/R@H-:LaserWriter 8f ChicagoNew YorkGenevaMonaco Zapf DingbatsPalatinoTimes HelveticaCourierSymbolGaramondI Garamond LightItalicB Garamond BoldBI Garamond BoldItalic HelveticaOverSonata Arial NarrowArial Book AntiquaBookman Old Style Courier NewMonotype CorsivaMonotype SortsCentury Schoolbook Wingdings Signote xSunday MT ExtravvO E @ OP^_pq1289QRYZpqwx..00000000112)3I3J344494=4>4}46j9: : :W>>>T>>>>>AABBBBBBBCHJJLL)L*LL\\]] ]A]B]M]O]]]]]]]]]]^^^n^o^~^^^^^^^^^_3_4_5_6_C_Errrrsss&s'scsdspsrsszz{${%{u{v34=>?H]il'(45?D]gn+349bcekt %EKhmx EGcnsq5T]^gk"#7>x{ 0124T *+4AEzCDjk !PQex} <=ij%?uv>?QRQROP}~;is,Iy*+,?@rs8:XY12STCDXrt$%1=wQg pqEFACDP->L".JR`f569CHUkp$%7COW`K[df^fg 789rt  ?]^_lmnPQRZ*+,[gy[QZcdezUdo~"#(35cpz#$(3{|!rstpq•¦§®!/<ST]`in{&2QX]ăęěĺ,łŃńř G`wƔƧƽ/02DžǛDZǸǽ 3OamȐȢȣȤȥȧȨȵȶȷȿ"*2|ɊɋɌɍɦɭ#$%8Xʺ&'(89:LOPY\d+,Emr̃̏02͇͈͉͕͊͋͐ͨͲM΂ΓΔ WXϯϲ(OUЃЈЉ7HқҜҡҳӝӵӻ !,9<FGIJLQSTXYo{ԀԊ԰<[\ctջ!"'1EPijklnqx|րHן׭׵]e؀؜؝ :ِٟ٠cځڢڣڤګcۂۃۄۇۍۧۨ۸ۻU`tܐܜܝܩܹܽݡݣݥݳݴݿCދޛޜޝޞު8KVel~ߟ߯ 4?e"*8He)*EPQ$%7TUFGXst89JK\]YZkl#$EFuv 78gh).3OPkl>??@)3OQRabefi67DGHQ[ 244Oai&)12bsz{ !"@C[|PR`n<=X\$s ).BDwxY_`bpq   39:W|'Cahil-.e#8<FNX| A4;</0:; ",GKm#$,  :Xesqw , /"/-!)/01312ZEgEh2\4-88081 <0!UEgEhY[vJ?[J@[JA[JB[JDo^JEonJFoJGoJHoJIoJKp,JPwUJQwJRwJS}JTJU}JV}Jk}Jn~Jo~Jp~.Jq~8Jr~sJv~Jw~>F^>Gc>_>`>>>Y>>>$>>>>>? 9E:E^EfEgElEc?.?4?=?O?X?h???DD D&DCDHDSDbD{D~DDDDE eXcRWE*E8JxJ%/* /xJJ2rJJ%SYTfUcgJKtKK3K<KI.?FNXcef,1EcdvGHUVqrOFG$%YZ]^de,-RSYsKMKNK4DGR]gmsKKKCQlyz[NOPd]ex^KKNN '2345XZxy35QRst56cdxDE[Xl #(612`a \^N`NN'N*8IW-9U]kqN1N2DGQVcy~yzN3NI   (1QwNQ/8:NSsNgNpNx2Nynƒ›¿Āŀƀ%'5ר߀åæç\cmvāĎĘğĠġĦ5@KU}ƊWƋƌƔIJXWYdXYLjdzǴǷN[345<>?iAghij!?HEYik|ʸNրֈ ֞֫ ֽ֬.9HIדןNNu=JTdEE<%RN:EIIBfwx $%.1:?LN=1dxNW W/2r>NWNXOkOrO,$%/Og;Nj|2JW1WFNX OAOO[MNgnW2P\W33WSW^WiWj4DOOObuOWkW"/=>\^uO]^WrqWFKL  ^ _ d v  W  ` x ~     P P  v  |+BuD Hh0x^yxZZ#Z6ZIZNx, $$G$[$$I$I$$$I$$P$12$$P $%  W%P'%`P5%Pc%%&& &;&W[[[x.\x^\j\k\xm\x\x]F&XWxWZxZSZxx&Y&\&b&|&}&&&&&&&PoPw&&&'''+','8'H'L'W'X(0(2(@(K  W(P(OZX(`1d(l>/Px>5J((PP 2(P(]g]h]|]]]]]]^^9^;^]^_^m^w^)^)/)=)M)j))^^))a,Px,eyhxxyy%,P,yry1y7,y|y}y-z$.z{/{0{M{_1{|2{3| 4|;5|j|{6|7|8|gl}9}[:}~~;~<;=i> ?y@A BsCDE<[qis%tFuuvwx4ycz{|}~-~FGb454<<+,*GDHQ[%M,!-E-L-R-Y-PPQ-^-ZY^.c.v.yZ^ZZZZZZZZZZ._ Q_#_$_H]._J^_la____baa.a....Q.Q//0QJ/x/////00(QK0_a0}a$01K1Y1[111a%11242D2I2Qa&1Y1[a6awaaaaaab$bRbb+akdLYsyz9[\g ,mlo -BFPXb_c~3 SF EnablerMichael GuzewiczMichael Guzewicz