The mid-1990s saw the release of a low-cost sound card known as the Ensoniq AudioPCI. This card uses a sample-based software MIDI synthesizer for internal "MIDI playback". The samples used by the synth are stored in "wavesets", which are files with an extension of .ECW. The manufacturer, Ensoniq (and later Sound Blaster, who bought Ensoniq and continued the AudioPCI product line with descendents like the Sound Blaster PCI64 and PCI128), never released an editor for these files, nor did they release any info on the file format. As an exercise, I reverse engineered the majority of the format in 2003-2004, with occasional revisions in 2009.
Ensoniq .ECW files store articulation and sample data used by the software synthesizer of sound cards equipped with an ES1370, ES1371, or ES1373 chip. These sound cards include the Ensoniq AudioPCI, the Sound Blaster! AudioPCI, the Sound Blaster! PCI64, the Sound Blaster! PCI128, and the Sound Blaster Live!. The file also includes metadata that is largely textual, some of which is unused by any known application. Additionally, the file seems to make use of obfuscation by employing multiple banks of extraneous pointers.
The "Configurator" (file name "ENSCFG32.EXE") is an auxilliary application included with the Ensoniq/Sound Blaster installation package. The Configurator lets the user switch between multiple wavesets via a GUI, rather than manually editing a configuration file or the Windows Registry. It also displays metadata stored in the waveset file, such as the name, version number, and any copyright information for the selected waveset.
The .ECW file uses Intel byte order (LSB first).
The byte counts below are given in decimal, not hexadecimal. When the "TYPE" is "string", the count is the length of the string in characters.
The .ECW file header stores the text that is shown when the waveset is loaded in the configurator. It also specifies the size and quantity of the other sections of the file. The file header is 1930 bytes long.
OFFSET Count TYPE Description
0000h 4 string File ID (always "ECLW")
0004h 4 string Spacer*
0008h 1 dword File offset where allocation chunk starts
000ch 1 dword Unknown. Always has value of 16.
0010h 80 string Copyright info for this waveset
0060h 80 string Waveset name
00b0h 256 string Filename of this waveset (ignored by the
configurator)
01b0h 80 string Description of this waveset
0200h 1280 string Information on this waveset. Usable length is
only 963 characters or so, because the
configurator will refuse to load the waveset
if characters 964 to 1280 of this string are
anything but nulls.
0700h 4 string Spacer
0704h 1 dword File offset where bank map starts
0708h 1 dword Length in bytes of the bank map (always 256)
070ch 1 dword Number of bank maps (always 1)
0710h 1 dword File offset where drum kit map starts
0714h 1 dword Length in bytes of the drum kit map (always
256)
0718h 1 dword Number of drum kit maps (always 1)
071ch 1 dword File offset where MIDI patch maps start
0720h 1 dword Total length in bytes of all MIDI patch maps
(always number of MIDI patch maps multiplied
by 256)
0724h 1 dword Number of MIDI patch maps
0728h 1 dword File offset where drum note maps start
072ch 1 dword Total length in bytes of all drum note maps
(always number of drum note maps multiplied by
256)
0730h 1 dword Number of drum note maps
0734h 1 dword File offset where instrument headers start
0738h 1 dword Total length in bytes of all instrument
headers (always number of instrument headers
multiplied by 23)
073ch 1 dword Number of instrument headers
0740h 1 dword File offset where patch headers start
0744h 1 dword Total length in bytes of all patch headers
(always number of patch headers multiplied by
76)
0748h 1 dword Number of patch headers
074ch 4 string Spacer
0750h 1 dword File offset where array #1 starts
0754h 1 dword Total length in bytes of all "slots" in
array #1 (always number of "slots" in
array #1, multiplied by 2)
0758h 1 dword Number of "slots" in array #1
075ch 1 dword File offset where array #2 starts
0760h 1 dword Total length in bytes of all "slots" in
array #2 (always number of "slots" in
array #2, multiplied by 2)
0764h 1 dword Number of "slots" in array #2
0768h 1 dword File offset where array #3 starts
076ch 1 dword Total length in bytes of all "slots" in
array #3 (always number of "slots" in
array #3, multiplied by 2)
0770h 1 dword Number of "slots" in array #3
0774h 1 dword File offset where sample headers start
0778h 1 dword Total length in bytes of all sample headers
(always number of sample headers multiplied by
16)
077ch 1 dword Number of sample headers
0780h 4 string Spacer
0784h 1 dword File offset where sample waveform area begins
0788h 1 dword Total length of sample waveform area in bytes
The bank map assigns one MIDI patch map to each of the 128 MIDI banks. The bank map is 256 bytes long.
OFFSET Count TYPE Description 0000h 1 word MIDI patch map assigned to MIDI bank #0 0002h 1 word MIDI patch map assigned to MIDI bank #1 0004h 1 word MIDI patch map assigned to MIDI bank #2 0006h 1 word MIDI patch map assigned to MIDI bank #3 ... 00feh 1 word MIDI patch map assigned to MIDI bank #127
OFFSET Count TYPE Description 0000h 1 word Drum note map assigned to MIDI drum kit #0 0002h 1 word Drum note map assigned to MIDI drum kit #1 0004h 1 word Drum note map assigned to MIDI drum kit #2 0006h 1 word Drum note map assigned to MIDI drum kit #3 ... 00feh 1 word Drum note map assigned to MIDI drum kit #127
The MIDI patch maps assign one instrument to each of the 128 MIDI patches. Each MIDI patch map corresponds to one or more MIDI banks. One MIDI patch map can be used by more than one MIDI bank; in fact, all of the MIDI banks can use the same MIDI patch map if desired (which would allow a smaller filesize and save system RAM). The number of MIDI patch maps is set in the .ECW file header. Each MIDI patch map is 256 bytes long.
OFFSET Count TYPE Description
0000h 1 word Instrument assigned to MIDI patch #0 when
using a MIDI bank whose entry in the bank map
is equal to 0
0002h 1 word Instrument assigned to MIDI patch #1, bank #0
0004h 1 word Instrument assigned to MIDI patch #2, bank #0
0006h 1 word Instrument assigned to MIDI patch #3, bank #0
...
00feh 1 word Instrument assigned to MIDI patch #4, bank #0
If there are multiple MIDI patch maps (according to the file header), then the MIDI patch maps must follow one another immediately with no gaps between:
OFFSET Count TYPE Description
0100h 1 word Instrument assigned to MIDI patch #0 when
using a MIDI bank whose entry in the bank map
is equal to 1
...
01feh 1 word Instrument assigned to MIDI patch #127 when
using a MIDI bank whose entry in the bank map
is equal to 1
0200h 1 word Instrument assigned to MIDI patch #0 when
using a MIDI bank whose entry in the bank map
is equal to 2
etc.
The drum note maps assign one instrument to each of the 128 MIDI drum notes. Each drum note map corresponds to one or more MIDI drum kits. One drum note map can be used by more than one drum kit; in fact, all of the drum kits can use the same drum note map if desired (which would allow a smaller filesize and save system RAM). The number of drum note maps is set in the .ECW file header. Each drum note map is 256 bytes long.
OFFSET Count TYPE Description
0000h 1 word Instrument assigned to MIDI drum note #0 when
using a drum kit whose entry in the drum kit
map is equal to 0
0002h 1 word Instrument assigned to MIDI patch #1, drum kit
#0
0004h 1 word Instrument assigned to MIDI patch #2, drum kit
#0
0006h 1 word Instrument assigned to MIDI patch #3, drum kit
#0
...
00feh 1 word Instrument assigned to MIDI patch #4, drum kit
#0
If there are multiple drum kit maps (according to the file header), then the drum kit maps must follow one another immediately with no gaps between:
OFFSET Count TYPE Description
0100h 1 word Instrument assigned to drum note #0 when
using a drum kit whose entry in the drum kit
map is equal to 1
...
01feh 1 word Instrument assigned to drum note #127 when
using a drum kit whose entry in the drum kit
map is equal to 1
0200h 1 word Instrument assigned to drum note #0 when
using a drum kit whose entry in the drum kit
map is equal to 2
etc.
The instrument headers assign one or more patches (that is, the INTERNAL patches used in the waveset, as opposed to MIDI patches, which are EXTERNAL) to each of the instruments. The instrument headers also affect certain properties of the instruments, such as tuning and pan. The number of instrument headers is set in the .ECW file header. Each instrument header is 23 bytes long.
OFFSET Count TYPE Description
0000h 1 char Determines the function of the remaining 22
bytes in the current instrument header
When the byte at offset 0000h of an instrument header has a value of 2, the rest of the header is arranged as follows:
OFFSET Count TYPE Description
0001h 1 char When set to a value of 0, only the first
instrument sub-header (see below) is active.
When set to 1, both sub-headers are used
simultaneously for each note played. When set
to 2, a split point is used to select which
of the sub-headers is used for a given note. A
value of 3 makes only the second sub-header
active. (Other values make neither active.)
0002h 1 char The note number above which only the second
instrument sub-header is used; otherwise, only
the first instrument sub-header is used. This
is only the case when the byte at offset 0001h
is set to a value of 2.
FIRST INSTRUMENT SUB-HEADER
0003h 1 int Patch assigned to this instrument sub-header
0005h 1 char Amplitude and envelope steepness (signed)
0006h 1 char Pan (signed; 193 seems to be extreme left and
64 seems to be extreme right)
0007h 1 char Coarse tune (signed; measured in semitones)
0008h 1 char Fine tune (signed; seems to be measured in
increments of 1/256 of a semitone)
0009h 1 int Amount of delay before onset of notes (seems
to be measured in miliseconds)
000bh 1 char Unknown. For tremolo strings and most if not
all drum kit percussion, has value of either
1 or 2.
000ch 1 char If, in two or more instrument sub-headers,
the byte at offset 000ch has the same value,
only one of the patches played by those
instrument sub-headers can sound
simultaneously. Examples of instruments where
this is desirable include open/closed hi-hats
and open/closed triangles.
SECOND INSTRUMENT SUB-HEADER (same as first)
000dh 1 int Patch assigned to this instrument sub-header
000fh 1 char Amplitude and envelope steepness (see above)
0010h 1 char Pan
0011h 1 char Coarse tune
0012h 1 char Fine tune
0013h 1 int Delay
0015h 1 char Unknown. For side stick and acoustic snare,
has value of 1.
0016h 1 char Probably same function as byte at offset
0000ch
When the byte at offset 0000h of an instrument header has a value of 255, the rest of the header is arranged as follows:
OFFSET Count TYPE Description
0001h 1 char Unknown.
0002h 1 int The number of another instrument header.
0004h 1 char When a MIDI note is played with a note number
less than or equal to the value of this byte,
the instrument header specified by the value
at offset 0002h is used. (One can think of
these bytes as IF-GOTO statements: IF the
note number <= the value at offset 0004h, GOTO
instrument header #X, where X is the value
stored at offset 0002h.)
0005h 1 int The number of another instrument header.
0007h 1 char When a MIDI note is played with a note number
less than or equal to the value of this byte,
the instrument header specified by the value
at offset 0005h is used.
0008h 1 int The number of another instrument header.
000ah 1 char When a MIDI note is played with a note number
less than or equal to the value of this byte,
the instrument header specified by the value
at offset 0008h is used.
000bh 1 int The number of another instrument header.
000dh 1 char When a MIDI note is played with a note number
less than or equal to the value of this byte,
the instrument header specified by the value
at offset 000bh is used.
...
0014h 1 int The number of another instrument header.
0016h 1 char When a MIDI note is played with a note number
less than or equal to the value of this byte,
the instrument header specified by the value
at offset 0014h is used. (The value here MUST
be 127.)
If there are multiple instrument headers (according to the file header), then the instrument headers must follow one another immediately with no gaps between.
The patch headers (which refers to the INTERNAL patches used in the waveset, as opposed to MIDI patches, which are EXTERNAL) assign one "slot" in array #1 to each patch. The patch headers also affect certain properties of the patches, such as the pitch and amplitude envelopes. The number of patch headers is set in the .ECW file header. Each patch header is 76 bytes long.
OFFSET Count TYPE Description
0000h 1 char Magnitude of pitch envelope (signed). Negative
values cause pitch to fall rather than rise.
A value of 0 effectively disables pitch
envelope.
0001h 1 char MIDI controller 1 (modulation) sensitivity
0002h 1 char Scale. A value of 0 denotes a 12-tones-per-
octave (i.e. chromatic) scale. A value of 1
causes this patch to ignore the MIDI note
number, so that every key on the keyboard is
the same pitch. A value of 2 denotes a 24-
tones-per-octave (i.e. quarter tone) scale.
0003h 8 string Unknown
000bh 1 int "Slot" in array #1 assigned to this patch
000dh 1 char Changes tuning slightly
000eh 2 string Unknown
0010h 1 char Shifts the split points of the samples played
by this patch
0011h 10 string Unknown
001bh 1 char Causes pitch to change more rapidly upon
note release when pitch envelope is enabled.
May be the destination of the release phase
of the pitch envelope.
001ch 1 char Delay before pitch envelope enters attack
phase
001dh 1 char Initial pitch for pitch envelope**
001eh 1 char Attack time for pitch envelope
001fh 1 char Attack level for pitch envelope
0020h 1 char Decay time for pitch envelope
0021h 1 char Decay level for pitch envelope
0022h 1 char Sustain time for pitch envelope
0023h 1 char Sustain level for pitch envelope
0024h 1 char Release time for pitch envelope
0025h 1 char Influence of MIDI note velocity on magnitude
of pitch envelope
0026h 1 char Unknown (may be proportional to attack time
of pitch envelope)
0027h 1 char Influence of MIDI note number on pitch
envelope time (0=none; 127=huge)
0028h 1 char When set to 0, the pitch envelope enters the
release phase when a MIDI note-off command
is received. When set to 1, pitch envelope
never enters the release phase.
0029h 2 string Unknown
002bh 1 char Delay before wavetable envelope enters attack
phase. Seems to play the final wavetable while
in this pre-attack phase.***
002ch 1 char Initial wavetable for wavetable envelope
002dh 1 char Attack time for wavetable envelope
002eh 1 char Attack level for wavetable envelope
002fh 1 char Decay time for wavetable envelope
0030h 1 char Decay level for wavetable envelope
0031h 1 char Sustain time for wavetable envelope
0032h 1 char Sustain level for wavetable envelope
0033h 1 char Release time for wavetable envelope
0034h 1 char Presumably influence of MIDI note velocity on
magnitude of wavetable envelope (I haven't
tested this)
0035h 1 string Unknown
0036h 1 char Presumably influence of MIDI note number on
wavetable envelope time (I haven't tested
this)
0037h 1 char Presumably, when set to 0, the wavetable
envelope enters the release phase when a MIDI
note-off command is received; when set to 1,
wavetable envelope never enters the release
phase (I haven't tested this)
0038h 1 string Unknown
0039h 1 char Possibly the destination of the release phase
of the amplitude envelope.
003ah 1 string Unknown
003bh 1 char Initial amplitude for amplitude envelope**
003ch 1 char Attack time for amplitude envelope
003dh 1 char Attack level for amplitude envelope
003eh 1 char Decay time for amplitude envelope
003fh 1 char Decay level for amplitude envelope
0040h 1 char Sustain time for amplitude envelope
0041h 1 char Sustain level for amplitude envelope
0042h 1 char Release time for amplitude envelope
0043h 1 char Influence of MIDI note velocity on magnitude
of amplitude envelope.
0044h 1 char Unknown. Seems to affect amplitude attack.
0045h 1 char Influence of MIDI note number on amplitude
envelope time.
0046h 1 char When set to 0, the amplitude envelope enters
the release phase when a MIDI note-off command
is received. When set to 1, amplitude envelope
never enters the release phase.
0047h 1 string Unknown
0048h 1 char Pitch LFO (i.e. vibrato) depth. A value above
0 will add pitch modulation to a patch even
when MIDI controller 1 (modulation) is set to
0.
0049h 1 char Pitch LFO (i.e. vibrato) speed.
004ah 1 char Delay before pitch LFO (i.e. vibrato) reaches
full depth.
004bh 1 string Unknown
The "slots" in array #1 assign one "slot" in array #3 to each "slot" in array #1. The number of "slots" in array #1 is set in the .ECW file header.
"SLOT" #0:
OFFSET Count TYPE Description
0000h 1 int "Slot" in array #3 assigned to this
"slot".
If there are multiple "slots" (according to the file header), then the "slots" must follow one another immediately with no gaps between.
"SLOT" #1:
OFFSET Count TYPE Description
0002h 1 int "Slot" in array #3 assigned to this
"slot".
"SLOT" #2:
OFFSET Count TYPE Description
0004h 1 int "Slot" in array #3 assigned to this
"slot".
etc.
The "slots" in array #2 are linked to the sample set info area. The number of "slots" in array #2 is set in the .ECW file header.
"SLOT" #0:
OFFSET Count TYPE Description
0000h 1 int Corresponds to value in sample set header.
The values tend to be extremely random, as
if to thwart reverse engineering of the .ecw
file format.
(Also seems to affect snare drums
in the 2 megabyte official waveset.)
If there are multiple "slots" (according to the file header), then the "slots" must follow one another immediately with no gaps between.
"SLOT" #1:
OFFSET Count TYPE Description
0002h 1 int Corresponds to value in sample set header.
The values tend to be extremely random, as
if to thwart reverse engineering of the .ecw
file format.
(Also seems to affect snare drums
in the 2 megabyte official waveset.)
"SLOT" #2:
OFFSET Count TYPE Description
0004h 1 int Corresponds to value in sample set header.
The values tend to be extremely random, as
if to thwart reverse engineering of the .ecw
file format.
(Also seems to affect snare drums
in the 2 megabyte official waveset.)
etc.
The "slots" in array #3 assign one sample header to each "slot" in array #3. The number of "slots" in array #3 is set in the .ECW file header.
"SLOT" #0: OFFSET Count TYPE Description 0000h 1 int Sample header assigned to this "slot". If there are multiple "slots" (according to the file header), then the "slots" must follow one another immediately with no gaps between. "SLOT" #1: OFFSET Count TYPE Description 0002h 1 int Sample header assigned to this "slot". "SLOT" #2: OFFSET Count TYPE Description 0004h 1 int Sample header assigned to this "slot". etc.
The sample headers indicate where the individual samples are to be found in the sample waveform area. They also specify how those samples are looped (if at all), and any split points between one sample and another. The number of sample headers is set in the .ECW file header. Each sample header is 16 bytes long.
OFFSET Count TYPE Description
0000h 1 char If a MIDI note is received with a MIDI note
number above this value, the next sample
header is used instead. If the MIDI note
number is still greater than the byte value at
offset 0000h of the next sample header, the
sample header after the next sample header is
used and so on until the MIDI note number is
less than or equal to the byte value at offset
0000h.
0001h 1 char Determines whether the sample is looped,
whether to apply tuning adjustments from patch
and instrument headers when the sample is used
as part of a drum kit, and whether to shift
loop points further into the sample waveform
area. When set to 0, no tuning adjustments are
made when the sample is used in a drum kit. A
value of 0 or 1 also disables looping. Values
of 2 or higher enable looping using the loop
points specified in the sample header (see
below). Values of 129 or higher shift the loop
points forward by an amount proportional to the
value minus 128 (i.e. 129 will shift the loop
points slightly, while 255 will shift them
quite far into the sample waveform area). Be
careful when experimenting with values over 129
as it is very possible to play past the end of
the sample waveform area, which may cause
Windows to crash.
0002h 1 char Fine tune (signed; seems to be in increments
of 1/256 of a semitone)
0003h 1 char Coarse tune (signed; measured in semitones)
0004h 1 dword Offset in sample waveform area where this
sample begins, multiplied by 8. Sample
waveform data can be shared by multiple sample
headers.
0008h 1 dword Offset in sample waveform area of this
sample's loop point, multiplied by 8.
000bh 1 dword Offset in sample waveform area of this
sample's end loop point, multiplied by 8
(fractional loop lengths permitted). This is
also where the sample ends when looping is
disabled. If a sample plays beyond the end of
the file, the operating system may lock up.
If there are multiple sample headers, then the sample headers must follow one another immediately with no gaps between.
The sample waveform area stores the actual sample waveform data used by the sound card's MIDI synthesizer. The sample waveform data is stored using 16-bit, mono, signed samples, with LSB first (Intel byte order). Theoretically, the sample waveform area can be up to 512 megabytes in size (sample start/loop/end loop points in the sample headers are specified using dwords, which have a maximum value of 2^32 or 4294967296. Diving this value by 8 - the sample start/ loop/end loop points are measured in 1/8 bytes - we get 536870912 bytes, or 512 megabytes. The practical limit, however, is about 16 megabytes--the configurator refuses to load wavesets larger than that. (Perhaps the sound card driver or ENSCFG32.EXE could be hacked to increase the effective file size limit?) The size of the sample waveform area is set in the .ECW file header.
In the wavesets provided by Ensoniq/Creative, the beginning of the sample waveform area is not used to store samples. Rather, it contains the names of each sample set in the remainder of the sample waveform area and other data pertaining to these sample sets (here, "sample sets" means a single sample or multiple samples if the split point for a sample--i.e. the byte in offset 0 of a sample header--is less than 127, in which case the sample set would "continue" to the following sample until a value of 127 was reached.) The AudioPCI's softsynth does not seem to need this section for playback, as changing the values in this section has little if any effect. I speculate that this area of the .ecw file was used by the proprietary software that Ensoniq/Creative used for designing the official wavesets, for displaying names for each sample set and such. I also speculate that the entire sample waveform area was at one point an Ensoniq file type itself, as the structure of the sample waveform area (as it exists in the wavesets provided by Ensoniq/Creative) resembles an independent file.
The beginning of the sample waveform area in the official .ecw files, which I will call the "Sample Set Info Area", has the following layout:
OFFSET Count TYPE Description
0000h 4 string Unknown (value of 16 in all wavesets provided
by Ensoniq/Creative)
0004h 1 dword Size of sample waveform area in bytes
0008h 4 string Unknown (always "RDNS")
000ch 1 char Unknown (always 14--length of sample set
names?)
000dh 1 char Always 22--probably length in bytes of each
sample set header
000eh 1 int Always 16--probably length in bytes of each
sample header in duplicate copy of Sample
Headers
0010h 1 int Always 40--probably offset where first sample
set header begins
0012h 1 int Offset in sample waveform area where sample set
headers begin
0014h 1 int Size of duplicate copy of Sample Headers in
bytes
0016h 1 int Number of sample sets
0018h 1 int Number of sample headers
001ah 14 string Unknown; "ENSONIQ ROM[null][null][null]" in
DOSTEST.ECW;
"[null]NSONIQ ROM[null][null][null]" in
all other wavesets provided by Ensoniq/Creative
"SAMPLE SET HEADER" #1:
0028h 1 dword Offset in the Sample Waveform Area where sample
header for the first sample in this sample set
is stored
002ch 1 int Corresponds to a "slot" number in array #1
002eh 1 int Corresponds to a value in array #2
0030h 14 string Name of this sample set. Null-terminated
string.
If there are multiple sample set headers (according to the value at offset 0x16 of the sample waveform area), then the sample set headers must follow one another immediately with no gaps between.
"SAMPLE SET HEADER" #2:
003eh 1 dword Offset in the Sample Waveform Area where sample
header for the first sample in this sample set
is stored
0042h 1 int Corresponds to a "slot" number in array #1
0044h 1 int Corresponds to a value in array #2
0046h 14 string Name of this sample set. Null-terminated
string.
"SAMPLE SET HEADER" #3:
0054h 1 dword Offset in the Sample Waveform Area where sample
header for the first sample in this sample set
is stored
0058h 1 int Corresponds to a "slot" number in array #1
005ah 1 int Corresponds to a value in array #2
005ch 14 string Name of this sample set. Null-terminated
string.
etc.
A duplicate copy of the Sample Headers follows the "Sample Set Headers". The actual sample waveform data seems to begin immediately afterward.
*A "spacer" is a series of 4 bytes of unknown purpose. The contents of a spacer are usually 01h, 00h, 01h, 00h; however, changing the contents does not seem to have any effect whatsoever. Only the size and placement of spacers matters; their contents do not.
**Most if not all of the values dealing with the envelopes are unsigned and confined to values between 0 and 127. Using values between 128 and 255 typically generates unpredictable results.
***The "wavetable envelope" transitions from one sample in a sample set to another. This is useful in creating evolving timbres without using long samples. This only seems to work with the "ELEC PIANO 2" (simulates decay of the upper partials) and "SAWTOOTH" (simulates a sweeping resonant filter) sample sets, however; I'm not sure why it doesn't do the same with any sample set.