The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# $Id: Key.pm,v 1.2 2004/12/04 18:15:58 root Exp $

package POE::Framework::MIDI::Key;

use strict;
no strict 'refs';
use vars '$VERSION'; $VERSION = '0.02';
use MIDI::Simple;

# a lexcion of key types and a key for building them from a root note
#
my $lexicon = {
    # major scale is tone tone semitone, tone tone tone semitone
    maj => [0,2,2,1,2,2,2,1], major => [0,2,2,1,2,2,2,1],
    # declaring it twice is probably dumb, but we need to be able to refer to it by both
    # key names......there's probaby a more sane way to do this
};

sub new {
    my (  $self, $class ) = ( {}, shift );
    bless $self, $class;
    $self->{cfg} 	= shift;
    #$self->make_root_numeric;
    $self->{intervals} = $lexicon->{$self->{cfg}->{name}} 
        or warn "sorry - $self->{cfg}->{name} is not supported yet";
    return $self;    
}

# the root note - the tonic of the key
sub root {
    my ( $self, $new_root ) = @_;
    $new_root ? 
        $self->{cfg}->{root} = $new_root  : return $self->{cfg}->{root}
}

#chord's name
sub name {
    my ( $self, $new_name ) = @_;
    $new_name ?
        $self->{cfg}->{name} = $new_name : return $self->{cfg}->{name}            
}

sub make_root_numeric {
    my $self = shift;    
    if ($self->{cfg}->{root} =~ /\D/) {
        $self->{cfg}->{root} = $self->note_to_number($self->{cfg}->{root});            
    }
}

sub note_to_number {
    my ( $self,  $note ) = @_;
    my ( $letters, $numbers ) = $note =~ /(\D+)(\d+)/; 
    my $position = $MIDI::Simple::Note{$letters};
    my $end_position = $position + ($numbers * 12);
    return $end_position; 
}

# what is the position of this note in the key?
sub noteposition {
    my ( $self, $note ) = @_;
    my $numeric_position = $self->note_to_number($note);
    print $self->root . "\n";
    my $scale = $self->numeric_scale;

    # we need to generate a "monster scale" that extends this tonal
    # pattern off to 0 and 127 
    for (@$scale) {
        print "$_\n";
    }        
}

# use the intervals from the lexicon to make the scale based on the
# root for this key.  For example, if use C4 for the root, (note #)
sub numeric_scale {
    my $self = shift;
    my @scale;
    my $last_note = $self->root;
    for (@{$self->{intervals}}) {
        push @scale, $last_note + $_;
        $last_note = $scale[-1]; # (negative subscripting)++
    }
    return \@scale;
}

sub intervals {
    my $self = shift;
    return $self->{intervals};    
}




1;

__END__

=head1 NAME

POE::Framework::MIDI::Key - Lexicon of key types

=head1 ABSTRACT

=head1 DESCRIPTION

This package provides a lexicon of key types (major, minor, etc) and 
an interval key for building them from a root note.  For example: 
consider a piano keyboard.  The space from any note to its next 
nearest neighbour might be a black key or a white key.  That's a 
semitone.  Two semitones, or the key next to the key's nearest 
neighbour is a Tone (or "Full Tone"). If you start on any key, and 
count up one semitone at a time, you get an ascending chromatic (play 
every semitone) scale.  If you count "Tone, Tone, Semitone, Tone, 
Tone, Tone, Semitone" from the starting note, you get a major scale. 
This module aims to provide easy calculations for things like "What's 
the third note in a major scale starting with C#?"

=head1 SYNOPSIS

  my $key = new POE::Framework::MIDI::Key({
      name => 'maj',
      root => 'E4'
  });

  print 'The third of the scale is ',
      ${$key->numeric_scale}[2], "\n";

=head1 SEE ALSO

L<POE>

L<MIDI::Simple>

L<http://justsomeguy.com/code/POE/POE-Framework-MIDI>

=head1 AUTHOR

Primary: Steve McNabb E<lt>steve@justsomeguy.comE<gt>

CPAN ID: SMCNABB

Secondary: Gene Boggs E<lt>cpan@ology.netE<gt>

CPAN ID: GENE

=head1 COPYRIGHT AND LICENCE

Copyright (c) 2002 Steve McNabb. All rights reserved.
This program is free software; you can redistribute it and/or modify 
it under the same terms as Perl itself.

The full text of the license can be found in the LICENSE file 
included with this module.

=cut