Music-AtonalUtil

 view release on metacpan or  search on metacpan

lib/Music/AtonalUtil.pm  view on Meta::CPAN


    # XXX packing not implemented beyond "right" method (via www.mta.ca docs)
    $self->{_packing} = 'right';    # $param{PACKING} // 'right';

    bless $self, $class;
    return $self;
}

sub normal_form {
    my $self = shift;
    my $pset = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];

    croak 'pitch set must contain something' if !@$pset;

    my %origmap;
    for my $p (@$pset) {
        push @{ $origmap{ $p % $self->{_DEG_IN_SCALE} } }, $p;
    }
    if ( keys %origmap == 1 ) {
        return wantarray ? ( [ keys %origmap ], \%origmap ) : [ keys %origmap ];
    }
    my @nset = sort { $a <=> $b } keys %origmap;

    my $equivs = $self->circular_permute( \@nset );
    my @order  = 1 .. $#nset;
    # NOTE this only performs 'right' packing, see commits
    # 9f0c33f8260af9584d38c92af4e7a6a39f7e2769 and prior for long since
    # unimplemented notes on this topic
    @order = reverse @order;

    my @normal;
    for my $i (@order) {
        my $min_span = $self->{_DEG_IN_SCALE};
        my @min_span_idx;

        for my $eidx ( 0 .. $#$equivs ) {
            my $span =
              ( $equivs->[$eidx][$i] - $equivs->[$eidx][0] ) % $self->{_DEG_IN_SCALE};
            if ( $span < $min_span ) {
                $min_span     = $span;
                @min_span_idx = $eidx;
            } elsif ( $span == $min_span ) {
                push @min_span_idx, $eidx;
            }
        }

        if ( @min_span_idx == 1 ) {
            @normal = @{ $equivs->[ $min_span_idx[0] ] };
            last;
        } else {
            @$equivs = @{$equivs}[@min_span_idx];
        }
    }

    if ( !@normal ) {
        # nothing unique, pick lowest starting pitch, which is first index
        # by virtue of the numeric sort performed above.
        @normal = @{ $equivs->[0] };
    }

    $_ += 0 for @normal;    # KLUGE avoid Test::Differences seeing '4' vs. 4

    return wantarray ? ( \@normal, \%origmap ) : \@normal;
}

# Utility, converts a pitch set into a scale_degrees-bit number:
#                7   3  0
# [0,3,7] -> 000010001001 -> 137
sub pcs2bits {
    my $self = shift;
    my $pset = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];

    croak 'pitch set must contain something' if !@$pset;

    my $bs = 0;
    for my $p ( map $_ % $self->{_DEG_IN_SCALE}, @$pset ) {
        $bs |= 1 << $p;
    }
    return $bs;
}

sub pcs2forte {
    my $self = shift;
    my $pset = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];

    croak 'pitch set must contain something' if !@$pset;

    return $PCS2FORTE{ join ',', @{ $self->prime_form($pset) } };
}

sub pcs2intervals {
    my $self = shift;
    my $pset = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];

    croak 'pitch set must contain at least two elements' if @$pset < 2;

    my @intervals;
    for my $i ( 1 .. $#{$pset} ) {
        push @intervals, $pset->[$i] - $pset->[ $i - 1 ];
    }

    return \@intervals;
}

sub pcs2str {
    my $self = shift;
    croak 'must supply a pitch set' if !defined $_[0];

    my $str;
    if ( ref $_[0] eq 'ARRAY' ) {
        $str = '[' . join( ',', @{ $_[0] } ) . ']';
    } elsif ( $_[0] =~ m/,/ ) {
        $str = '[' . $_[0] . ']';
    } else {
        $str = '[' . join( ',', @_ ) . ']';
    }
    return $str;
}

sub pitch2intervalclass {
    my ( $self, $pitch ) = @_;



( run in 0.497 second using v1.01-cache-2.11-cpan-f4a522933cf )