The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Music::Scala - Scala scale support for Perl

SYNOPSIS

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

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

  $scala->set_concertfreq(422.5);
  $scala->interval2freq(0, 1); # (422.5, 445.1)

  $scala->set_description('Heavenly Chimes');
  $scala->set_notes(qw{ 32/29 1/2 16/29 });
  $scala->write_scala('chimes.scl');

  # or cents, note the quoting on .0 value
  $scala->set_notes(250.9, 483.3, 715.6, 951.1, '1200.0');

  # MIDI equal temperament algos for comparison
  $scala->pitch2freq(69);
  $scala->freq2pitch(440);

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 specification, along with an archive of scala files that define various tunings and temperaments.

METHODS

Methods will die or croak under various conditions, mostly related to bad input. new would be a good one to start with.

freq2pitch frequency

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

get_binmode

Returns the current binmode layer setting, undef by default.

get_concertfreq

Returns the concert frequency presently set in the object. 440 (Hz) is the default.

get_concertpitch

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

get_description

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

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 negative)) or otherwise integer ratios (e.g. 3/2 or 2).

  $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).

get_ratios

Returns, as a list, the "notes" of the scala, except all converted to ratios (notes may either be interval numbers or values in cents). Throws an exception if the notes have not been set by some previous method.

interval2freq intervals ...

Converts a list of passed interval numbers (list or single array reference) to frequencies (in Hz) that are returned as a list. Interval numbers are integers, 0 for unison, 1 for the first interval (which would be a minor 2nd for a 12-note scale, but something different for scales of other sizes), and so on up to an octave or moral equivalent thereof, depending on the scale. 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.

Conversions are based on the concertfreq setting, which is 440Hz by default. Use set_concertfreq to adjust this, for example to base the conversion around the frequency of MIDI pitch 60:

  $scala->set_concertfreq(261.63);

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

The output frequencies may need to be rounded because of floating point math:

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

  $scala->interval2freq(1);   # 879.999999999999

  sprintf "%.2f", $scala->interval2freq(1); # 880.00
new optional_params, ...

Constructor. Returns object. Accepts various optional parameters.

  • binmode - sets a default binmode layer that will be used when reading scala files (unless that read_scala call has a different binmode passed to it). Given that the scala file specification demands "ISO 8859-1 'Latin-1' or the ASCII subset", and uses Internet linefeeds, a reasonable default would be:

      Music::Scala->new( binmode => ':encoding(iso-8859-1):crlf' );

    Output encoding may also need to be set, if in particular the description field of the scala definition will be printed or saved elsewhere. See perluniintro for details. Note that both read_scala and write_scala will use this same global binmode value if no binmode is passed to those methods.

  • concertfreq - sets the reference value (in Hertz) for conversions using the interval2freq method. By default this is 440Hz.

  • file - filename, if specified, that will be passed to read_scala.

  • fh - file handle, if specified, that will be passed to read_scala, but only if file is not specified.

  • MAX_LINES - sets the maximum number of lines to read while parsing data. Sanity check high water mark in the event bad input is passed.

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 (equal temperament), as influenced by the concertfreq setting.

read_scala filename

Parses a scala file. Will throw some kind of exception if anything at all is wrong with the input. Use the 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 );

Returns the Music::Scala object, so can be chained with other calls.

set_binmode binmode_layer

Sets the default binmode layer used in read_scala and write_scala methods (unless a custom binmode argument is passed to those calls). Returns the Music::Scala object, so can be chained with other calls.

set_concertfreq frequency

Sets the concert frequency to the specified value (in Hz). Will throw an exception if the input does not look like a positive number.

set_concertpitch pitch_number

Sets the MIDI pitch number tied to the concertfreq. Changing this will affect the freq2pitch and pitch2freq methods.

set_description description

Sets the description. Should be a string. Returns the Music::Scala object, so can be chained with other calls.

set_notes array_or_array_ref

Sets the notes. Can be either an array, or an array reference, ideally containing values in ratios or cents as per the Scala scale file specification, and the method will throw an exception if these ideals are not met. Returns the Music::Scala object, so can be chained with other calls.

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 (that nothing is removing automatically) 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.

Returns the Music::Scala object, so can be chained with other calls.

EXAMPLES

Print names of any scala files whose note count is 12 (only about 29% of the scales.zip as of 2013-02-20).

  #!/usr/bin/env perl
  use strict;
  use warnings;
  use feature qw/say/;
  
  use Music::Scala ();
  my $s = Music::Scala->new;
  
  for my $file ( glob('*.scl') ) {
    eval { say $file if $s->read_scala($file)->get_notes == 12 };
    warn "could not parse '$file': $@" if $@;
  }

Another interesting question is which scales contain the octave (and whether that octave is also the ultimate element of the note list). This requires converting the "notes" into actual ratios, as otherwise an octave might be represented as 2/1 or 1200.0 or 4/2 or so forth. Roughly 87% of the scales are bounded at the octave:

  for my $file ( glob('*.scl') ) {
    my @ratios = $s->read_scala($file)->get_ratios;
    if ( $ratios[-1] == 2 ) { say $file }
  }

SEE ALSO

http://www.huygens-fokker.org/scala/ by Manuel Op de Coul, and the scala archive http://www.huygens-fokker.org/docs/scales.zip.

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 many other more in-depth treatments stemming from the more than one centuries of development behind these topics).

http://github.com/thrig/Music-Scala for the perhaps more current version of this code, or to report bugs, etc.

AUTHOR

Jeremy Mates, <jmates@cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2013 by Jeremy Mates

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.16 or, at your option, any later version of Perl 5 you may have available.