Jeremy Mates

NAME

Music::Scala - Scala scale support for Perl

SYNOPSIS

  use Music::Scala ();
  my $scala = Music::Scala->new;

  $scala->set_binmode(':encoding(iso-8859-1):crlf');
  $scala->read_scala('groenewald_bach.scl');

  $scala->get_description;   # "Jurgen Gronewald, si..."
  $scala->get_notes;         # (256/243, 189.25008, ...)
  $scala->get_cents;
  $scala->get_ratios;

  # which interval is what frequency given the loaded scala data
  # and the given concert frequency (A440 if not changed)
  $scala->set_concertfreq(422.5);
  $scala->interval2freq(0, 1); # (422.5, 445.1)

  $scala->set_description('Heavenly Chimes');
  # by ratio; these are strings that get parsed internally
  $scala->set_notes(qw{ 32/29 1/2 16/29 });
  $scala->write_scala('chimes.scl');

  # or by cents -- mark well the quoting on 1200; Perl will map
  # things like a bare 1200.000 to '1200' which then becomes the
  # ratio 1200/1 which is wrong.
  $scala->set_notes(250.9, 483.3, 715.6, 951.1, '1200.0');

  # utility MIDI equal temperament algorithms
  $scala->pitch2freq(69);
  $scala->freq2pitch(440);

See also the eg/ and t/ directories of the distribution of this module for example code.

DESCRIPTION

Scala scale support for Perl: reading, writing, setting, and interval to frequency conversion methods are provided. The "SEE ALSO" section links to the developer pages for the original specification, along with an archive of scala files that define various tunings and temperaments.

SEVERAL WORDS REGARDING FLOATING POINT NUMBERS

Frequencies derived from scala scale calculations will likely need to be rounded due to floating point number representation limitations:

  # octave, plus default concert pitch of 440, so expect 880
  my $scala = Music::Scala->new->set_notes('1200.000');

  $scala->interval2freq(1);   # 879.999999999999        (maybe)

  sprintf "%.*f", 0, $scala->interval2freq(1);   # 880  (for sure)

For more information, see:

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html "What Every Computer Scientist Should Know About Floating-Point Arithmetic". David Goldberg, Computing Surveys, March 1991.

CONSTRUCTOR

The new method accepts any of the "ATTRIBUTES" as well as optionally one of the file => filename or fh => filehandle arguments to read a scala scale file from.

  $scala = Music::Scala->new(
    binmode => ':encoding(iso-8859-1):crlf',
    file    => 'foo.scl',
  );

ATTRIBUTES

Moo attributes via has statements. These may throw exceptions if bad data is passed (negative frequencies or the like). The sporadic use of get_* and set_* accessors is due to compatibility with older versions of this module. These may also be specified to new:

  # equivalent ways to do the same thing
  $scala = Music::Scala->new( concertfreq => 443 );
  $scala->set_concertfreq(443);
binmode (get_binmode, set_binmode(binmode_layer))

Gets or sets the default binmode layer used in the read_scala and write_scala methods (unless a custom binmode argument is passed to those calls). The scala scale files from www.huygens-fokker.org tend to be in the ISO 8859-1 encoding, mostly for the description and other such metadata fields. Note that Perl on Windows systems tends to turn on :crlf. For scala scale files, it probably should be specified, regardless of the operating system. Therefore, a reasonable default to set might be:

  $scala->set_binmode(':encoding(iso-8859-1):crlf');

Though this module does nothing by default for encoding.

concertfreq (get_concertfreq, set_concertfreq(frequency))

Gets or sets the concert frequency. 440 (Hz) is the default.

concertpitch (get_concertpitch, set_concertpitch(pitch))

Gets or sets the MIDI pitch number that the concertfreq maps to. 69 by default (as that is the MIDI number of A440).

description (get_description, set_description("blah blah"))

Gets or sets the description of the scala data. This will be the empty string if no description was read or set prior.

MAX_LINES

Gets or sets the maximum lines to allow in an input scala scale file, 3000 by default.

notes

Gets or sets the notes from the scala scale data loaded, if any. Mostly for internal use; the get_cents, get_notes, or get_ratios methods are likely better means to access this information, and the read_scala, set_by_frequency, or set_notes methods better ways to set it.

METHODS

Methods will throw exceptions under various conditions, mostly related to bad input or scala scale data not being loaded.

cents2ratio cents, [ precision ]

Converts a value in cents (e.g. 1200) to a ratio (e.g. 2). An optional precision for sprintf can be supplied; the default precision is 2. There are 1200 cents in an octave (a doubling of the frequency).

freq2pitch frequency

Converts the passed frequency (Hz) to the corresponding MIDI pitch number using the MIDI algorithm, as influenced by the concertfreq attribute. Unrelated to scala, but handy for comparison with results from interval2freq.

This method *is not* influenced by the scala scale data, and always uses equal temperament. See also pitch2freq.

get_cents

Returns, as a list, the "notes" of the scala scale data, except converted to cents ("notes" may either be ratios or values in cents; this method ensures that they are all represented in cents). Throws an exception if the notes have not been set by some previous method call (one of the read_scala, set_by_frequency, or set_notes methods).

get_notes

Returns, as a list, the "notes" of the scala, but throws an exception if this field has not been set by some previous method. The notes are either real numbers (representing values in cents, or 1/1200 of an octave (these may be rarely be negative)) or otherwise integer ratios (e.g. 3/2 or 2 (these may not be negative)).

  $scala->read_scala(file => $some_file);
  my @notes = $scala->get_notes;
  if (@notes == 12) { ...

The implicit 1/1 for unison is not contained in the list of notes; the first element is for the 2nd degree of the scale (e.g. the minor second of a 12-tone scale). Other implementations may differ in this regard.

get_ratios

Returns, as a list, the "notes" of the scala, except converted to ratios ("notes" may either be ratios or values in cents; this method ensures that these values are returned as ratios). Throws an exception if the notes have not been set by some previous method call.

interval2freq intervals ...

Converts a list of passed interval numbers (as a list or a single array reference) to frequencies (in Hz) that are returned as a list. Interval numbers are integers, 0 for unison (the concertfreq), 1 for the first interval (which would be a "minor 2nd" for a 12-note scale, but something different for scales of different composition), and so on up to the "octave." Negative intervals take the frequency in the other direction, e.g. -1 for what in a 12-note system would be a minor 2nd downwards. Intervals past the "octave" consist of however many "octaves" are present in the scale, plus whatever remainder lies inside that "octave," if any. "octave" uses scare quotes due to 13% of the scala archive consisting of non-octave bounded scales; that is, scales that do not repeat themselves when the frequency is doubled (see the is_octavish method for a test for that condition).

Conversions are based on the concertfreq setting, which is 440Hz by default. Use set_concertfreq method to adjust this. An example that derives the frequencies of C4 through B4 using the equal temperament tuning file from the scala scale file archive:

  $scala->read_scala('equal.scl');
  $scala->set_concertfreq(261.63);
  my @freqs = map { sprintf '%.2f', $_ } 
    $scala->interval2freq(qw/0 1 2 3 4 5 6 7 8 9 10 11/);

Some scala files note what this value should be in the comments or description, or it may vary based on the needs of the software or instruments involved.

There is no error checking for nonsense conditions: an interval of a 15th makes no sense for a xylophone with only 10 keys in total. Audit the contents of the scala scale file to learn what its limits are or screen for appropriate scales depending on the application.

is_octavish

Returns true if the scala scale has an ultimate ratio of 2:1, as well as no negative or repeated ratios; false otherwise. Throws an exception if no scala scale is loaded.

notes2cents notes ...

Given a list of notes, returns a list of corresponding cents. Used internally by the get_cents method.

notes2ratios notes ...

Given a list of notes, returns a list of corresponding ratios. Used internally by the get_ratios and interval2freq methods.

pitch2freq MIDI_pitch_number

Converts the given MIDI pitch number to a frequency using the MIDI conversion algorithm, as influenced by the concertfreq setting.

This method *is not* influenced by the scala scale data, and always uses equal temperament. See also freq2pitch.

ratio2cents ratio, [ precision ]

Converts a ratio (e.g. 2) to a value in cents (e.g. 1200). An optional precision for sprintf can be supplied; the default precision is 2.

read_scala filename

Parses a scala file. Will throw some kind of exception if anything at all is wrong with the input. Use the appropriate get_* methods to obtain the scala data thus parsed. Comments in the input file are ignored, so anything subsequently written using write_scala will lack those. All ratios are made implicit by this method; that is, a 2 would be qualified as 2/1.

As an alternative, accepts also file or fh hash keys, along with binmode as in the new method:

  $scala->read_scala('somefile');
  $scala->read_scala( file => 'file.scl', binmode => ':crlf' );
  $scala->read_scala( fh   => $input_fh );
set_by_frequency root_frequency, frequencies...

Given a root frequency as the first argument, performs the equivalent of set_notes except that it creates the intervals on the fly based on the root_frequency supplied. Handy if you have a list of frequencies, and need those converted to cents or ratios.

set_notes array_or_array_ref

Sets the notes. Can either be an array, or an array reference, ideally containing values in ratios or cents as per the Scala scale file specification, as an exception will be thrown if these ideals are not met.

NOTE cents with no value past the decimal must be quoted in code, as otherwise Perl converts the value to 1200 which the code then turns into the integer ratio 1200/1 instead of what should be 2/1. read_scala does not suffer this problem, as it is looking for the literal dot, and that is a different code path than what happens for ratios.

  $scala->set_notes(250.9, 483.3, 715.6, 951.1, '1200.0');
write_scala filename

Writes a scala file. Will throw some kind of exception if anything at all is wrong, such as not having scala data loaded in the object. Like read_scala alternatively accepts file or fh hash keys, along with a binmode option to set the output encoding.

  $scala->write_scala('out.scl');
  $scala->write_scala( file => 'out.scl', binmode => ':crlf' );
  $scala->write_scala( fh => $output_fh );

Data will likely not be written until the fh passed is closed. If this seems surprising, see http://perl.plover.com/FAQs/Buffering.html to learn why it is not.

EXAMPLES

Check the eg/ and t/ directories of the distribution of this module for example code.

BUGS

Reporting Bugs

If the bug is in the latest version, send a report to the author. Patches that fix problems or add new features are welcome.

http://github.com/thrig/Music-Scala

Known Issues

Negative cents are likely not handled well, or at all. The specification frowns on negative ratios, but does allow for negative cents, so converting such negative cents to ratios (which do not support negative values) might yield unexpected or wrong results. Only 0.36% of the scala scale archive file scales contain negative cents.

SEE ALSO

http://www.huygens-fokker.org/scala/ by Manuel Op de Coul, and in particular the scala archive http://www.huygens-fokker.org/docs/scales.zip which contains many different scales to play around with.

Scales, tunings, and temperament would be good music theory topics to read up on, e.g. chapters in "Musicmathics, volume 1" by Gareth Loy, among other in-depth treatments stemming from the no few centuries of development behind music theory.

AUTHOR

thrig - Jeremy Mates (cpan:JMATES) <jmates at cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2013-2015 by Jeremy Mates

This module is free software; you can redistribute it and/or modify it under the Artistic License (2.0).