Why not adopt me?
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 but here standardised to lower-case), 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, manufacturer, 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_data and tune, which have scalar values.
See: doc/gravis.txt doc/headers.c doc/timidity/instrum.c doc/timidity/instrum.h doc/timidity/playmidi.c and doc/wav2pat.c for details of what the values mean...
See: test.pl 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 hundreths of Hz?). I would have expected a MIDI pitch there, like low=48 high=72, corresponding to the SoundFont key range parameter, allowing different wavsamples to be used for different tessituras. The number 12288.0 in TiMidity's doc/timidity/playmidi.c may hold a clue. See: perl examples/sf_list gravis/fiddle.pat | less
FUNCTIONS
- 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.
- 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.
- 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
Two 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.
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
man timidity - (1) MIDI-to-WAVE converter and player
man timidity.cfg - (5) configure file of TiMidity++