!/usr/bin/perl

sflist - what instruments does a soundfont file contain?

use 5.36.0;

use File::Format::RIFF;

my $show_header;

if ( @ARGV == 2 and $ARGV[0] eq '-h' ) {

$show_header = 1;

shift;

}

die "Usage: sflist [-h] soundfont-file\n" unless @ARGV == 1;

open my $fh, '<', $ARGV[0] or die "open failed '$ARGV[0]': $!\n";

my $ffr;

eval {

$ffr = File::Format::RIFF->read($fh);

1;

} or do {

die "sflist: unable to parse '$ARGV[0]'\n";

};

if ($show_header) {

my %info = (

    ICMT => 'Rem',

    ICRD => 'Date',

    IENG => 'Author',

    INAM => 'Name',

    IPRD => 'Product',

    ISFT => 'Tool',

    ifil => 'SFv',

    irom => 'ROM',

    isng => 'Engine',

    iver => 'ROMv',

);

my $list = getchunk( 3, sfbk => $ffr, 0 );

my $type = $list->type;

die "sflist: expected INFO chunk (got '$type')\n"

  unless $type eq 'INFO';

my $count = $list->numChunks;

die "sflist: unexpected INFO count $count\n" if $count <= 0;

my $copyright;

for my $i ( 0 .. $count - 1 ) {

    my $chunk = $list->at($i);

    my $id    = $chunk->id;

    if ( $id eq 'ifil' or $id eq 'iver' ) {

        my ( $major, $minor ) = unpack 'vv', $chunk->data;

        say "$info{$id}\t$major.$minor";

    } elsif ( $id eq 'ICOP' ) {

        # this one can spam the terminal so is shown last

        $copyright = unpack 'Z*', $chunk->data;

    } elsif ( $id =~ m/^(?:INAM|isng|IENG|ISFT|ICMT|IPRD|ICRD|irom)$/ ) {

        my $s = unpack 'Z*', $chunk->data;

        say "$info{$id}\t", $s if length $s;

    } else {

        say '??? ', $chunk->dump( $chunk->size );    # TODO

    }

}

say "\n", $copyright if defined $copyright and length $copyright;

exit;

}

my $pdta = getchunk( 3, sfbk => $ffr, 2 );

my $phdr = getchunk( 9, pdta => $pdta, 0 );

my $id = $phdr->id;

die "sflist: unknown PHDR ID '$id' (want 'phdr')\n" if $id ne 'phdr';

my $phdr_size = $phdr->size;

die "sflist: invalid PHDR size $phdr_size\n"

if $phdr_size < 76

or $phdr_size % 38 != 0;

my %presets;

my $data = $phdr->data;

$phdr_size -= 38; # last header is a "End Of Presets" terminator

my $offset = 0;

while ( $offset < $phdr_size ) {

# full record is Z20vvvVVV

my ( $name, $preset, $bank ) = unpack "x${offset}Z20vv", $data;

$presets{$bank}{$preset} = $name;

$offset += 38;

}

say "BANKS ", scalar keys %presets;

for my $bank ( sort { $a <=> $b } keys %presets ) {

print "BNK $bank";

my $ref = $presets{$bank};

for my $preset ( sort { $a <=> $b } keys %$ref ) {

    say "\t$preset\t$ref->{$preset}";

}

}

sub getchunk ( $wantcount, $wanttype, $object, $index ) {

my $type = $object->type;

die "sflist: unexpected type '$type' (want '$wanttype')\n"

  if $type ne $wanttype;

my $count = $object->numChunks;

die "sflist: unexpected count $count (want $wantcount)\n"

  if $count != $wantcount;

return $object->at($index);

}

Proxy Information
Original URL
gemini://thrig.me/music/soundfont/sflist.pl
Status Code
Success (20)
Meta
text/plain
Capsule Response Time
1066.160851 milliseconds
Gemini-to-HTML Time
2.165118 milliseconds

This content has been proxied by September (3851b).