# Copyright (c) 2001 Douglas Sparling. All rights reserved. This program is free
# software; you can redistribute it and/or modify it under the same terms
# as Perl itself.
package Lutherie::FretCalc;
use strict;
use vars qw($VERSION);
$VERSION = '0.30';
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
if (defined $_[0]) {
$self->{scale} = $_[0];
} else {
$self->{scale} = 25;
}
$self->{num_frets} = 24;
$self->{fret_num} = 12;
$self->{in_units} = 'in';
$self->{out_units} = 'in';
$self->{calc_method} = 't';
$self->{tet} = 12;
#$self->{half_fret} = ();
bless($self, $class);
return $self;
}
sub scale {
my($self) = shift;
if(@_) { $self->{scale} = shift }
return $self->{scale};
}
sub num_frets {
my($self) = shift;
if(@_) { $self->{num_frets} = shift }
return $self->{num_frets};
}
sub fret_num {
my($self) = shift;
if(@_) { $self->{fret_num} = shift }
return $self->{fret_num};
}
sub in_units {
my($self) = shift;
if(@_) { $self->{in_units} = shift }
return $self->{in_units};
}
sub out_units {
my($self) = shift;
if(@_) { $self->{out_units} = shift }
return $self->{out_units};
}
sub calc_method {
my($self) = shift;
if(@_) { $self->{calc_method} = shift }
return $self->{calc_method};
}
sub tet {
my($self) = shift;
if(@_) { $self->{tet} = shift }
return $self->{tet};
}
sub half_fret {
my($self) = shift;
#if(@_) { $self->{half_fret} = shift }
if(@_) {
if($self->{half_fret}) {
$self->{half_fret} = join(',', $self->{half_fret},shift);
} else {
$self->{half_fret} = shift;
}
}
return $self->{half_fret};
}
sub fretcalc {
my($self) = shift;
if(@_) { $self->{num_frets} = shift }
my $distance_from_nut = 0;
my $distance_from_nut_formatted;
my @chart;
$chart[0] = sprintf("%8.4f",0) if $self->{out_units} eq 'in';
$chart[0] = sprintf("%8.1f",0) if $self->{out_units} eq 'mm';
for my $i (1..$self->{num_frets}) {
if ($self->{calc_method} eq 't') {
$distance_from_nut = ($self->{scale} - $self->{scale}/2 ** ($i/$self->{tet}));
} elsif ($self->{calc_method} eq 'ec') {
my $x = ($self->{scale} - $distance_from_nut) / 17.817;
$distance_from_nut += $x;
} elsif ($self->{calc_method} eq 'es') {
my $x = ($self->{scale} - $distance_from_nut) / 17.835;
$distance_from_nut += $x;
} elsif ($self->{calc_method} eq 'ep') {
my $x = ($self->{scale} - $distance_from_nut) / 18;
$distance_from_nut += $x;
} else {
$distance_from_nut = ($self->{scale} - $self->{scale}/2 ** ($i/12));
}
### input scale: in, output scale: in
if( ($self->{in_units} eq 'in') && ($self->{out_units} eq 'in') ) {
$distance_from_nut_formatted = sprintf("%8.4f",$distance_from_nut);
### input scale: in, output scale: mm
} elsif( ($self->{in_units} eq 'in') && ($self->{out_units} eq 'mm') ) {
$distance_from_nut *= 25.4;
$distance_from_nut_formatted = sprintf("%8.1f",$distance_from_nut);
### input scale: mm, output scale: in
} elsif( ($self->{in_units} eq 'mm') && ($self->{out_units} eq 'in') ) {
$distance_from_nut /= 25.4;
$distance_from_nut_formatted = sprintf("%8.4f",$distance_from_nut);
#### input scale: mm, out_units: mm
} else {
$distance_from_nut_formatted = sprintf("%8.1f",$distance_from_nut);
}
push @chart, $distance_from_nut_formatted;
}
return @chart;
}
sub fret {
my $self = shift;
# Check if fret_num was passed
if(@_) { $self->{fret_num} = shift }
my $distance_from_nut = 0;
my $distance_from_nut_formatted;
if ($self->{calc_method} eq 't') {
$distance_from_nut = ($self->{scale} - $self->{scale}/2 ** ($self->{fret_num}/$self->{tet}));
} elsif ($self->{calc_method} eq 'ec') {
for my $i (1..$self->{fret_num}) {
my $x = ($self->{scale} - $distance_from_nut) / 17.817;
$distance_from_nut += $x;
}
} elsif ($self->{calc_method} eq 'es') {
for my $i (1..$self->{fret_num}) {
my $x = ($self->{scale} - $distance_from_nut) / 17.835;
$distance_from_nut += $x;
}
} elsif ($self->{calc_method} eq 'ep') {
for my $i (1..$self->{fret_num}) {
my $x = ($self->{scale} - $distance_from_nut) / 18;
$distance_from_nut += $x;
}
} else {
$distance_from_nut = ($self->{scale} - $self->{scale}/2 ** ($self->{fret_num}/$self->{tet}));
}
### in_units: in, out_units: in
if( ($self->{in_units} eq 'in') && ($self->{out_units} eq 'in') ) {
$distance_from_nut_formatted = sprintf("%8.4f",$distance_from_nut);
### in_units: in, out_units: mm
} elsif( ($self->{in_units} eq 'in') && ($self->{out_units} eq 'mm') ) {
$distance_from_nut *= 25.4;
$distance_from_nut_formatted = sprintf("%8.1f",$distance_from_nut);
### in_units: mm, out_units: in
} elsif( ($self->{in_units} eq 'mm') && ($self->{out_units} eq 'in') ) {
$distance_from_nut /= 25.4;
$distance_from_nut_formatted = sprintf("%8.4f",$distance_from_nut);
### in_units: mm, out_units: mm
} else {
$distance_from_nut_formatted = sprintf("%8.1f",$distance_from_nut);
}
return $distance_from_nut_formatted;
}
sub dulc_calc {
my($self) = shift;
my %dulc;
my @frets = $self->fretcalc(24); # Use 24 frets for dulcimer
# Set standard frets
$dulc{1} = $frets[2];
$dulc{2} = $frets[4];
$dulc{3} = $frets[5];
$dulc{4} = $frets[7];
$dulc{5} = $frets[9];
$dulc{6} = $frets[10];
$dulc{7} = $frets[12];
$dulc{8} = $frets[14];
$dulc{9} = $frets[16];
$dulc{10} = $frets[17];
$dulc{11} = $frets[19];
$dulc{12} = $frets[21];
$dulc{13} = $frets[22];
$dulc{14} = $frets[24];
# Add the half frets (valid = 1,6,8,13)
my @half_frets = split(/,/,$self->{half_fret});
foreach my $half( @half_frets ) {
if( $half == 1 ) {
$dulc{1.5} = $frets[3];
} elsif( $half == 6 ) {
$dulc{6.5} = $frets[11];
} elsif( $half == 8 ) {
$dulc{8.5} = $frets[15];
} elsif( $half == 13 ) {
$dulc{13.5} = $frets[23];
}
}
return %dulc;
}
1;
__END__
=head1 NAME
Lutherie::FretCalc - Calculate stringed instrument fret locations
=head1 SYNOPSIS
use Lutherie::FretCalc;
my $fretcalc = Lutherie::FretCalc->new($scale_length);
$fretcalc->in_units('in');
$fretcalc->out_units('in');
my $fret = $fretcalc->fret($fret_num);
my @chart = fretcalc($num_frets);
=head1 DESCRIPTION
C<Lutherie::FretCalc> provides two methods for calculating fret spacing
locations for stringed musical instruments. C<fret()> will find the distance
from the nut for a given fret number. C<fretcalc()> will generate an array
containing the fret locations for the given number of frets.
=head1 OVERVIEW
=over 4
=item TODO
=back
=head1 CONSTRUCTOR
=over 4
=item new ( SCALE_LENGTH );
This is the constructor for a new Lutherie::FretCalc object. C<SCALE_LENGTH>
is the numeric value for the scale length to be used for the calculation.
The default value for scale length is 25.
The unit can be set with the C<in_units()> and C<out_units()> methods.
The default is 'in' (inches).
=back
=head1 METHODS
=over 4
=item scale ( [ SCALE_LENGTH ] )
If C<SCALE_LENGTH> is passed, this method will set the scale length.
The default value is 25. The scale length is returned.
=item num_frets ( [ NUM_FRETS ] )
If C<NUM_FRETS> is passed, this method will set the number of frets.
The default value is 24. The number of frets is returned.
=item fret_num ( [ FRET_NUM ] )
If C<FRET_NUM> is passed, this method will set the fret number.
The default value is 12. The fret number is returned.
=item in_units ( [ IN_UNITS ] )
If C<IN_UNITS> is passed, this method will set the in units.
The default is 'in' (inches). The in unit is returned.
'in' - Inches, 'mm' - Millimeters
=item out_units ( [ OUT_UNITS ] )
If C<OUT_UNITS> is passed, this method will set the out units.
The default is 'in' (inches). The out unit is returned.
'in' - Inches, 'mm' - Millimeters
=item calc_method ( [ CALC_METHOD ] )
If C<CALC_METHOD> is passed, this method will set the calc method.
The default is 't' (tempered). The calc method is returned.
't': tempered - power of $i/$tet (default),
'ec': classic - 17.817,
'es': Sloane - 17.835,
'ep': Primative - 18
=item tet ( [ TET ] )
If C<TET> is passed, this method will set the tones per ocatave.
The default is 12. The number of tones per ocatave is returned.
This value is only valid when using calc_method = 't'.
=item half_fret ( [ FRET_NUM ] )
If C<FRET_NUM> is passed, this method will calculate the half fret for this fret number. Valid values are 1, 6, 8 and 13. Only used with C<dulc_calc()>. A comma separated list of half frets is returned.
=item fret ( [ FRET_NUM ] )
Calculate the distance from the nut to the fret number. A scalar containing the fret location for C<FRET_NUM> is returned.
=item fretcalc ( [ NUM_FRETS ] )
Calculate fret locations for given scale length, number of frets, calc type
and tet. An ordered array containing fret locations from 1 to C<NUM_FRETS> is returned.
=item dulc_calc ( )
Calculate fret locations for mountain dulcimer. Number of frets is set at 14.
Half frets may be added by using C<half_fret> function. Valid half frets are 1+, 6+, 8+ and 13+. C<num_frets> will be ignored when using C<dulc_calc>. A hash containing fret locations from 1 to 14 is returned.
=back
=head1 AUTHOR
Doug Sparling, doug@dougsparling.com
=head1 COPYRIGHT
Copyright (c) 2001 Douglas Sparling. All rights reserved. This program is free
software; you can redistribute it and/or modify it under the same terms
as Perl itself.
=cut