MIDI::Morph - Musical transition tool


MIDI-Morph documentation Contained in the MIDI-Morph distribution.

Index


Code Index:

NAME

Top

MIDI::Morph - Musical transition tool

VERSION

Top

Version 0.01

SYNOPSIS

Top

    use MIDI::Morph;

    my $m = MIDI::Morph->new(from => $from_score, to => $to_score);
    $new_score = $m->Morph(0.4);

DESCRIPTION

Top

The aim of MIDI::Morph is to provide an easy-to-use composition tool that allows transitions between two gestalten (musical snippets). The data handled by this module is in MIDI::Score format (at this moment, only note events are considered).

This is an alpha release, features and API will be extended and changed iteratively.

CONSTRUCTOR

Top

new

    my $m = MIDI::Morph->new(from => $from, to => $to);

Creates a new morpher object.

METHODS

Top

AutoMap

AutoMap is called automatically by MIDI::Morph and provides a mapping from the notes in the from structure to the notes in the to structure. Currently, it is a simple mapping 1st<->1st, 2nd<->2nd, but this will become more sophisticated in future.

Morph

    $m->Morph($position);

Morph creates a structure that reflects a transition point between from (0) and to (1). Currently the transition is linear.

FUNCTIONS

Top

event_distance

    MIDI::Morph::event_distance($event1, $event2, $weights);

This function calculates the distance between two events. The events passed should be note events as described in MIDI::Score. The weights are passed as a hash reference with the keys start, end, pitch and velocity. This parameter is optional; the default weights are 1, 1, 1 and 0 respectively.

These weights can be used in case you want to measure the distance between two events in different terms.

morph_single_event

    my $event = morph_single_event($from_event, $to_event, $position);

This helper function morphs two single events.

AUTHOR

Top

Christian Renz, <crenz @ web42.com>

BUGS

Top

Please report any bugs or feature requests to bug-midi-morph@rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=MIDI-Morph. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SEE ALSO

Top

MIDI

COPYRIGHT & LICENSE

Top


MIDI-Morph documentation Contained in the MIDI-Morph distribution.
package MIDI::Morph;

use warnings;
use strict;

our @ISA       = qw(Exporter);
our @EXPORT_OK = qw(event_distance);

our $VERSION = '0.02';

sub new {
    my $class  = shift;
    my %params = @_;
    my $self   = {};

    return undef
      unless ref $params{from} eq 'ARRAY' && ref $params{to} eq 'ARRAY';

    foreach (qw(from to)) {
        $self->{$_} = $params{$_};
    }

    return bless $self, $class;
}

sub AutoMap {
    my ($self) = @_;

    $self->{map} = [];

    foreach (0 .. $#{$self->{from}}) {
        $self->{map}->[$_] = [$_];
    }
}

sub Morph {
    my ($self, $position) = @_;

    $self->AutoMap()
      unless (ref $self->{map});

    my @morph = ();
    foreach (0 .. $#{$self->{map}}) {
        push @morph,
          morph_single_event($self->{from}->[$_], $self->{to}->[$_], $position);
    }

    return [@morph];
}

our %distance_default_weights = (
    start    => 1,
    end      => 1,
    pitch    => 1,
    velocity => 0);

our %distance_weights = %distance_default_weights;

sub event_distance {
    my ($a, $b, $weights) = @_;

    # 'note', position, duration, channel, pitch, velocity
    # 0       1         2         3        4      5
    return undef unless ref $a eq 'ARRAY' && ref $b eq 'ARRAY';
    return undef unless scalar @$a == 6 && scalar @$a == 6;
    return undef unless $a->[0] eq 'note' && $b->[0] eq 'note';

    my %weights = %distance_weights;

    if (ref $weights eq 'HASH') {
        foreach (keys %weights) {
            $weights{$_} = $weights->{$_} if defined $weights->{$_};
        }
    }

#    use Data::Dumper qw(Dumper);
#    print STDERR "\n\n" . Dumper({
#        weights => \%weights,
#        a => $a,
#        b => $b
#    }).  "\n\n";

    return
      abs($a->[1] - $b->[1]) * $weights{start} +
      abs(($a->[1] + $a->[2]) - ($b->[1] + $b->[2])) * $weights{end} +
      abs($a->[4] - $b->[4]) * $weights{pitch} +
      abs($a->[5] - $b->[5]) * $weights{velocity};
}

sub morph_single_event {
    my ($from, $to, $position) = @_;

    return undef unless ref $from  eq 'ARRAY' && ref $to  eq 'ARRAY';
    return undef unless $from->[0] eq 'note'  && $to->[0] eq 'note';

    my @event = @$from;

    # leave channel untouched, change start, duration, pitch, velocity
    foreach (1, 2, 4, 5) {
        my $diff = $to->[$_] - $from->[$_];
        $event[$_] = $from->[$_] + $position * $diff;
    }

    return [@event];
}

42;