💾 Archived View for thrig.me › blog › 2023 › 08 › 11 › midiwarble.pl captured on 2023-09-08 at 17:06:59.

View Raw

More Information

-=-=-=-=-=-=-

#!/usr/bin/env perl
# midiwarble - how does the pitch_wheel_change thing even work? map a
# sin() function to pitch_wheel_change events so that the effect can be
# heard and different devices tested. the actual message splits the
# value across two bytes, and some devices may only use some portion of
# those bytes
use 5.36.0;
use Getopt::Long 'GetOptions';
use Math::Trig ':pi';
use MIDI;

# ranges for some of these are detailed in the MIDI::Event docs. the
# patch should be instruments that sustain (woodwinds, strings) and
# probably not the wood block or snare drum
#
# things to check here include how small can range be before you can't
# hear the changes, what happens as you maximize the range, what
# different patch sound like, what happens when the step is tiny but the
# range very large, etc
my $chan     = 0;       # MIDI channel
my $circle   = 12;      # how many $step to complete a circle
my $midinum  = 69;      # A note
my $patch    = 16;      # drawbar organ
my $range    = 1000;    # -8192 .. 8191, though see the MIDI docs
my $repeat   = 3;       # how many times to go through a circle
my $step     = 24;      # MIDI dtime between wheel change events
my $ticks    = 96;      # some MIDI thing
my $velocity = 120;     # pretty loud

GetOptions(
    'circle=i'   => \$circle,
    'channel=i'  => \$chan,
    'patch=i'    => \$patch,
    'pitch=i'    => \$midinum,
    'range=i'    => \$range,
    'step=i'     => \$step,
    'ticks=i'    => \$ticks,
    'velocity=i' => \$velocity,
) or exit 64;
my $file = shift // 'out.midi';

my @events = (
    [ 'patch_change', 0, $chan, $patch ],
    [ 'note_on', 0, $chan, $midinum, $velocity ]
);
my $when  = 0;
my $whole = $step * $circle;
my $max   = $whole * $repeat;
while ( $when <= $max ) {
    my $wheel = $range * sin( $when * pi2 / $whole );
    # timidity shows text_event so those may be good to mark when the
    # wheel is what, but fluidsynth (or at least my wrapper for it) does
    # not show those
    push @events, [ 'pitch_wheel_change', $step, $chan, $wheel ];
    $when += $step;
}
push @events, [ 'note_off', $step, $chan, $midinum, 0 ];

MIDI::Opus->new(
    {   format => 0,
        ticks  => $ticks,
        tracks => [ MIDI::Track->new( { events => \@events } ) ]
    }
)->write_to_file($file);