The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

MIDI::SoundFont - Handles .sf2 SoundFont and .pat and .zip Gravis files

SYNOPSIS

 use MIDI::SoundFont();
 use Data::Dumper(Dumper);
 $Data::Dumper::Indent = 1;  $Data::Dumper::Sortkeys = 1;

 my %sf = MIDI::SoundFont::file2sf('doc/Jeux14.sf2');
 open (P, '|-', 'less'); print P Dumper(\%sf); close P;
 MIDI::SoundFont::sf2file('/tmp/Jeux15.sf2', %sf);

 my %gus = MIDI::SoundFont::file2gravis('gravis/Gravis.zip');
 open (P, '|-', 'less'); print P Dumper(\%gus); close P;
 MIDI::SoundFont::gravis2file('/tmp/Gravis2.zip', %gus);

 print MIDI::SoundFont::timidity_cfg('/home/me/Gr.zip',%gus);
 print MIDI::SoundFont::timidity_cfg('/home/me/Sf.sf2',%sf);

DESCRIPTION

This module offers a Perl interface to ease the manipulation of SoundFont and Gravis files.

This module loads these files into a Perl associative array whose structure is documented in the section IN-MEMORY SOUNDFONT FORMAT or IN-MEMORY GRAVIS FORMAT below.

Nothing is exported by default, but all the documented functions can be exported, e.g.: use MIDI::SoundFont(file2sf, sf2file);

No functions are provided to manipulate the .pat members in a Gravis .zip archive; to do this work you should use Archive::Zip directly.

Future versions should offer translation between Gravis and SoundFont formats, and should also allow importing a .wav snippet into a patch by automatically detecting the optimal StartLoop and EndLoop points. These features are currently unimplemented.

IN-MEMORY SOUNDFONT FORMAT

See:

 perl examples/sf_list doc/Jeux14.sf2 | less
 perl examples/sf_list -b 0 -p 17 -l  doc/Jeux14.sf2 | less

file2sf($filename) returns a hash with keys: ifil, isng, INAM, irom, iver, ICRD, IENG, IPRD, ICOP, ICMT and ISFT which have scalar values (see http://www.pjb.com.au/midi/sfspec21.html#i5 ), and the keys: phdr whose value is an arrayref, and inst and shdr whose values are hashrefs.

Each item of the phdr array is a Preset ("Preset" is a SoundFont term which means substantially the same as the MIDI "Patch"), which is a hashref with the following keys: achPresetName is the Patch-name, wBank is the MIDI Bank-number, wPreset is the MIDI Patch-number ( see http://www.pjb.com.au/midi/sfspec21.html#7.2 ), plus pbags which is an arrayref. Each pbag is a hashref with the following keys: modulators which is an arrayref ( see http://www.pjb.com.au/midi/sfspec21.html#7.4 ) and generators which is a hashref ( see http://www.pjb.com.au/midi/sfspec21.html#7.5 ). The generators is where most of the action is ( see http://www.pjb.com.au/midi/sfspec21.html#8.1.3 ), and particularly crucial is instrument which tells the Patch (i.e. Preset) which Instrument it will be using.

Each key of the inst hash is an Instrument-name, ( see http://www.pjb.com.au/midi/sfspec21.html#7.6 ), and each value is an Instrument ( see http://www.pjb.com.au/midi/sfspec21.html#8.5 ), which is a hashref with just one key: ibags whose value is an arrayref. Each ibag is a hashref with the following keys: modulators which is an arrayref ( see http://www.pjb.com.au/midi/sfspec21.html#7.8 ) and generators which is a hashref ( see http://www.pjb.com.au/midi/sfspec21.html#7.9 ). The generators is where most of the action is ( see http://www.pjb.com.au/midi/sfspec21.html#8.1.3 ), and particularly crucial is sampleID which (at last!) tells the Instrument and hence the Preset which Sample it will be using :-)

Each item of the shdr array is a Sample which is a hashref with the following keys, which all have scalar values: achSampleName, dwStart, dwEnd, dwStartloop, dwEndloop, dwSampleRate, byOriginalKey, chCorrection, wSampleLink and sfSampleType ( see http://www.pjb.com.au/midi/sfspec21.html#7.10 ), plus sampledata, which (at last!!) contains the (16-bit signed little-endian) audio data.

The Patch-names ( achPresetName ) must be unique, the Instrument-names ( achInstName ) must be unique, and the Sample-names ( achSampleName ) must be unique.

IN-MEMORY GRAVIS FORMAT

See: perl examples/sf_list gravis/fiddle.pat | less

file2gravis($filename) returns a hash with keys: description, filename, num_channels, and num_voices, which have scalar values, and instruments whose value is an arrayref (although in practice I've never met a patch-file with more than one instrument). The instrument is a hash with keys: instr_name and instr_num, which have scalar values, and layers whose value is an arrayref. Each layer has keys: id and previous, which have scalar values (apparently unused), and wavsamples whose value is an arrayref. Each wavsample is a hash with keys: balance, data, envelope_data, high_freq, loop_end, loop_start, low_freq, mode, root_freq, sample_name, sample_rate, scale_factor, scale_freq, tremolo_depth, tremolo_phase, tremolo_sweep, vibrato_depth, vibrato_ctl, vibrato_sweep and tune, which have scalar values.

Unlike the SoundFont format, the frequencies low_freq, high_freq and root_freq are in thousandths of a Hz, and the loop_start and loop_end are in bytes, not samples.

The mode bits describes the format of the data, and the following package variables can be imported with use MIDI::SoundFont(':CONSTS'); MODES_16BIT=1 MODES_UNSIGNED=2 MODES_LOOPING=4 MODES_PINGPONG=8 MODES_REVERSE=16 MODES_SUSTAIN=32 MODES_ENVELOPE=64 MODES_CLAMPED=128

See: doc/gravis.txt doc/headers.c doc/timidity/instrum.c doc/timidity/instrum.h doc/timidity/playmidi.c doc/wav2pat.c and ftp://ftp.gravis.com/Public/Sdk/ for more details of what the values mean. The tremolo and vibrato data displayed by timidity -idvv -x 'bank 0\n0 ./gravis/fiddle.pat' are different from the values of the tremolo and vibrato variables above, because the timidity variables have been multiplied by corresponding control ratios: see doc/timidity/instrum.c

See: test.pl, examples/make_bank5 and examples/sf_list for examples manipulating this data-structure.

SOUNDFONT FILE-FORMAT

Fortunately, there exists authoritative and clear documentation of the SoundFont file format: http://connect.creativelabs.com/developer/SoundFont/sfspec21.pdf Unfortunately, it's a fairly hard format to work with...

A SoundFont-2 compatible RIFF file comprises three chunks: an INFO-list chunk containing a number of required and optional sub-chunks describing the file, its history, and its intended use, see http://www.pjb.com.au/midi/sfspec21.html#i5

an SDTA-list chunk comprising a single sub-chunk containing any referenced digital audio samples, see http://www.pjb.com.au/midi/sfspec21.html#i6

and a PDTA-list chunk containing nine sub-chunks which define the articulation of the digital audio data, see http://www.pjb.com.au/midi/sfspec21.html#i7

GRAVIS FILE-FORMAT

The files doc/gravis.txt, doc/headers.c and doc/wav2pat.c disagree somewhat about the file format. Most authoritative is the TiMidity source in doc/timidity/, but it's also somewhat hard to interpret. The format adopted here seems to work with all patches in gravis/Gravis.zip

Several of the parameters seem obscure: for example, num_channels is often zero, when it should be either 1 or 2, and instr_num is either zero or, in non-Gravis patches, usually random. In the wavsample section, low_freq and high_freq seem large (perhaps in thousandths of Hz? See: ftp://ftp.gravis.com/Public/Sdk/PATCHKIT.ZIP). These are the parameters that must correspond to the SoundFont key range, allowing different wavsamples to be used for different tessituras. See: perl examples/sf_list gravis/fiddle.pat | less

FUNCTIONS

%sf = file2sf($filename)

Reads the file, which should be a SoundFont file, and converts it to the data-structure documented above in the IN-MEMORY SOUNDFONT FORMAT section.

The filename can also be a URL, or - meaning STDIN

sf2file($filename, %soundfont)

Converts a data-structure as documented above in the IN-MEMORY SOUNDFONT FORMAT section into a file as documented in the SOUNDFONT FILE-FORMAT section.

%sf = new_sf($inam)

Returns a minimal empty soundfont data-structure as documented above in the IN-MEMORY SOUNDFONT FORMAT section. The optional argument $inam sets the 'INAM' value. You can then change $sf{'INAM'} and $sf{'phdr'}[0]{'wPreset'} or push onto @{$sf{'phdr'}} and so on. See examples/make_bank5 in the examples/ directory.

%gr = file2gravis($filename)

Reads the file, which should be either a Gravis .pat patch-file, or a .zip archive of patch-files, and converts it to the data-structure documented above in the IN-MEMORY GRAVIS FORMAT section.

The filename can also be a URL, or - meaning STDIN

gravis2file($filename, %gravis)

Converts a data-structure as documented above in the IN-MEMORY GRAVIS FORMAT section either into a .pat patch-file as documented in the GRAVIS FILE-FORMAT section, or into a .zip archive of patch-files.

%sf = new_pat()

Returns a minimal empty patch data-structure; the reference to this is a value in the gravis data-structure documented above in the IN-MEMORY GRAVIS FORMAT section, the key being the filename it will get when given a home in a .zip file. See examples/make_bank5 in the examples/ directory.

timidity_cfg($filename, %sf_or_gravis)

This returns a suggested timidity.cfg paragraph to allow you to use your soundfont, or gravis patch or zip, in timidity. The filename is the .sf2 or .pat or .zip file in which it resides, or will reside.

You should insert the resulting string into your timidity.cfg by hand, using your favourite text editor, because there are bound to be things you'll want to change.

For Gravis .zip archives, the String::Approx module is used to guess some General-Midi-conformant patch-numbers.

EXAMPLES

Five simple examples in the examples/ subdirectory are already useful applications:

sf_list

sf_list displays, in a readable format, a list of the Patches available in a .sf2 SoundFont file, or in a Gravis .zip archive, or the contents of a Gravis .pat patch-file. It displays the Patches in a readable format, e.g.: bank 8 patch 17 # Detuned Organ 2

It also has options -l for long, detailed output, and -b and -p to restrict the choice to particular Banks and Patches, and a -c option to suggest a paragraph for your timidity.cfg

sf_edit

sf_edit is a Term::Clui application which allows certain simple operations such as moving Banks, deleting Patches.

make_bank5

make_bank5 puts together a SoundFont file from scratch, using some simple waveforms, and then puts together some substantially identical Gravis patches. These files can be used successfully both by timidity and by csound, which is a reasonable test of MIDI::SoundFont's conformance to the file-formats. See: http://www.pjb.com.au/midi/free/Bank5.sf2 http://www.pjb.com.au/midi/free/SawtoothToTriangle.pat and http://www.pjb.com.au/midi/free/SquareToSine.pat

csound_scoresynth.csd

csound_scoresynth.csd evolves from the script explained on page 148 of the book Csound Power by Jim Aikin. It shows how to load a SoundFont into csound and play its notes directly from the Score section of the .csd file.

It assumes you have run make_bank5, so that the SoundFont /tmp/Bank5.sf2 already exists.

csound_midisynth.csd

csound_midisynth.csd evolves from the script fluidcomplex.csd by Istvan Varga, as included in the csound documentation. It shows how to load a SoundFont into csound and play its notes using a midi keyboard, which you will have to connect by hand using some command such as aconnect ProKeys 14:0

It assumes you have run make_bank5, so that the SoundFont /tmp/Bank5.sf2 already exists.

DOWNLOAD

This module is available from CPAN at http://search.cpan.org/perldoc?MIDI::SoundFont

AUTHOR

Peter J Billam, http://www.pjb.com.au/comp/contact.html

SEE ALSO

 http://search.cpan.org/perldoc?MIDI::SoundFont
 http://search.cpan.org/perldoc?File::Format::RIFF
 http://search.cpan.org/perldoc?File::Format::RIFF::Container
 http://search.cpan.org/perldoc?Archive::Zip
 http://search.cpan.org/perldoc?File::Temp
 http://search.cpan.org/perldoc?String::Approx
 http://connect.creativelabs.com/developer/SoundFont/sfspec21.pdf
 http://www.pjb.com.au/midi/sfspec21.html
 http://www.onicos.com/staff/iz/timidity/dist/tools-1.1.0/wav2pat.c
 http://timidity.sourceforge.net
 ftp://ftp.gravis.com/Public/Sdk/
 http://www.csounds.com/manual/html/fluidEngine.html
 http://www.csounds.com/manual/html/fluidNote.html
 http://www.csounds.com/manual/html/fluidLoad.html
 "Csound Power" by Jim Aikin, Course Technology, Cengage Learning, 2013,
   ISBN-13 978-1-4354-6004-1
   ISBN-10 1-4354-6004-9
 man timidity         - (1) MIDI-to-WAVE converter and player
 man timidity.cfg     - (5) configure file of TiMidity++