Module: Musa::Scales::Scales

Defined in:
lib/musa-dsl/music/scales.rb

Overview

Scale system registry.

The Scales module provides a central registry for scale systems, allowing access by symbol ID or method name.

Registration

Scale systems register themselves using Scales.register:

Scales.register EquallyTempered12ToneScaleSystem, default: true

Access Methods

By symbol:

Scales[:et12]              # => EquallyTempered12ToneScaleSystem
Scales[:et12][440.0]       # => ScaleSystemTuning with A=440Hz

By method name:

Scales.et12                # => EquallyTempered12ToneScaleSystem
Scales.et12[440.0]         # => ScaleSystemTuning with A=440Hz

Default system:

Scales.default_system                      # => The default scale system
Scales.default_system.default_tuning       # => Default tuning (A=440Hz)

Examples:

Accessing scale systems

# The three ways reach the same system
Scales[:et12]           # => Musa::Scales::EquallyTempered12ToneScaleSystem
Scales.et12             # => Musa::Scales::EquallyTempered12ToneScaleSystem
Scales.default_system   # => Musa::Scales::EquallyTempered12ToneScaleSystem

Working with tunings

# Get tuning with A=440Hz (default)
tuning = Scales[:et12][440.0]

# Get tuning with baroque pitch A=415Hz
baroque = Scales[:et12][415.0]

Building scales

tuning = Scales.default_system.default_tuning

# C major scale
c_major = tuning.major[60]

# A minor scale
a_minor = tuning.minor[69]

c_major.root.pitch  # => 60
a_minor.root.pitch  # => 69

# The number in the brackets IS the root pitch: a scale kind is asked for
# a root and answers with the scale rooted there.

Class Method Summary collapse

Class Method Details

.default_systemClass<ScaleSystem>

Returns the default scale system.

Examples:

Scales.default_system  # => EquallyTempered12ToneScaleSystem

Returns:

  • (Class<ScaleSystem>)

    the default ScaleSystem subclass



167
168
169
# File 'lib/musa-dsl/music/scales.rb', line 167

def self.default_system
  @default_scale_system
end

.extend_metadata(scale_kind_id, **metadata) ⇒ Hash

Convenience method to extend metadata for a scale kind by ID.

Finds the ScaleKind class by its ID symbol and adds custom metadata to it. This is a shortcut for accessing the class directly and calling extend_metadata.

Examples:

Scales.(:major, my_tag: :favorite)
Scales.(:dorian, mood: :nostalgic, suitable_for: [:jazz])

Parameters:

  • scale_kind_id (Symbol)

    the scale kind identifier (e.g., :major, :dorian)

  • metadata (Hash)

    key-value pairs to add as custom metadata

Returns:

  • (Hash)

    the updated custom_metadata hash

Raises:

  • (KeyError)

    if scale kind not found

See Also:



186
187
188
189
190
191
192
193
194
# File 'lib/musa-dsl/music/scales.rb', line 186

def self.(scale_kind_id, **)
  system = default_system
  raise KeyError, "No default scale system registered" unless system

  klass = system.scale_kind_class(scale_kind_id)
  raise KeyError, "Scale kind :#{scale_kind_id} not found" unless klass

  klass.(**)
end

.get(id) ⇒ Class<ScaleSystem> Also known as: []

Retrieves a registered scale system by ID.

Examples:

Scales[:et12]  # => EquallyTempered12ToneScaleSystem

Parameters:

  • id (Symbol)

    the scale system identifier

Returns:

Raises:

  • (KeyError)

    if scale system not found



151
152
153
154
155
# File 'lib/musa-dsl/music/scales.rb', line 151

def self.get(id)
  raise KeyError, "Scale system :#{id} not found" unless @scale_systems.key?(id)

  @scale_systems[id]
end

.register(scale_system, default: nil) ⇒ self

Registers a scale system.

Makes the scale system available via symbol lookup and dynamic method. Optionally marks it as the default system.

Examples:

# This is how the library registers its own, at load time:
#   Scales.register EquallyTempered12ToneScaleSystem, default: true
#
# Doing it again is refused -- an id names one system for the whole
# process, and a scale already built from it would not notice a swap.
Scales.register Musa::Scales::EquallyTempered12ToneScaleSystem
# => ArgumentError

Parameters:

  • scale_system (Class<ScaleSystem>)

    the ScaleSystem subclass to register

  • default (Boolean) (defaults to: nil)

    whether to set as default system

Returns:

  • (self)

Raises:

  • (ArgumentError)

    if a system with that id is already registered



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/musa-dsl/music/scales.rb', line 123

def self.register(scale_system, default: nil)
  @scale_systems ||= {}

  if (existing = @scale_systems[scale_system.id])
    raise ArgumentError,
          "scale system #{scale_system.id.inspect} is already registered " \
          "as #{existing.name}; unregister it first if you mean to replace it"
  end

  @scale_systems[scale_system.id] = scale_system

  @default_scale_system = scale_system if default

  self.class.define_method scale_system.id do
    scale_system
  end

  self
end