HeadMusic
The head_music Ruby gem provides a toolkit for working with Western music theory. Model and manipulate the fundamental elements of music including pitches, scales, key signatures, intervals, and chords.
Features
- Western Music Theory Fundamentals: Work with pitches, scales, intervals, chords, and key signatures
- Musical Analysis: Analyze harmonic progressions, voice leading, and counterpoint
- Style Analysis: Rules for species counterpoint and voice leading
- Internationalization: Support for multiple languages (English, French, German, Italian, Russian, Spanish)
- Instrument Modeling: Extensive database of musical instruments with ranges and properties
Installation
Add this line to your application’s Gemfile:
ruby
gem 'head_music'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install head_music
Quick Start
```ruby require ‘head_music’
Work with pitches and intervals
pitch = HeadMusic::Rudiment::Pitch.get(‘C4’) higher_pitch = HeadMusic::Rudiment::Pitch.get(‘E4’) interval = HeadMusic::Analysis::DiatonicInterval.new(pitch, higher_pitch) puts interval.name # => “major third”
Create scales
scale = HeadMusic::Rudiment::Scale.get(‘C’, :major) puts scale.pitches.map(&:to_s) # => [“C4”, “D4”, “E4”, “F4”, “G4”, “A4”, “B4”]
Analyze chords
pitches = %w[C4 E4 G4].map { |p| HeadMusic::Rudiment::Pitch.get(p) } chord = HeadMusic::Analysis::PitchSet.new(pitches) puts chord.major_triad? # => true ```
Style Analysis
Look up a style guide by key and analyze a voice against it. Style::Guide.get returns nil for an
unknown key, so a stored key can be validated before use.
```ruby guide = HeadMusic::Style::Guide.get(‘first_species_harmony’) guide.category # => :harmony guide.display_name # => “First Species Harmony”
assessment = guide.assess(voice) assessment.fitness # => 0.0 to 1.0 assessment.messages # => [“Prefer contrary motion. Move voices in different melodic directions.”] ```
A guide declares its guidelines in three tiers, and the tier decides how much each one counts:
ruby
guide.gate_items # preconditions -- can this voice be assessed at all?
guide.primary_items # what the guide is about
guide.secondary_items # background craft it inherits rather than teaches
guide.guide_items # all three, in that order
A gate asks whether the voice can be assessed at all. Failing one stops the assessment — the rubric is not computed, and the grade is the gates alone:
ruby
assessment.assessable? # => false for a voice too short to judge, or with no companion voice
assessment.fitness # => the gates' product; the rubric was never reached
Among the rules that are assessed, primaries share φ⁻¹ of the rubric and secondaries share φ⁻², which is why a species guide weighs its own rules as heavily as all the craft it inherits put together. The budgets are fixed rather than divided by item count, so what a guide teaches does not thin out as it inherits more. A rubric that declares only one tier is renormalized to the full range.
Within a tier, a second axis: strength. A prohibition (:strong, the default) weighs twice a
preference (:weak), normalized by that tier’s own total. Strength never crosses a tier boundary, and
it is inert on gates, which multiply the whole rubric:
ruby
HeadMusic::Style::Guidelines::NoParallelPerfectOnDownbeats.strength # => :strong
HeadMusic::Style::Guidelines::PreferContraryMotion.strength # => :weak
Unlike tier, strength is a property of the guideline rather than of the list it was declared in — a
preference is a preference in every guide that names it. An item may override it for the
tradition-dependent case, with Guideline.with(strength: :weak).
Each entry is a Style::GuideItem — a guideline plus the configuration this guide gives it — and
assessing one yields a frozen Style::GuideItemAssessment:
```ruby item = guide.primary_items.first item.guideline # => HeadMusic::Style::Guidelines::NoUnisonsInMiddle item.config # => {} item.strength # => :strong
assessment.guide_item_assessments.first.tier # => :gate assessment.guide_item_assessments.first.strength # => :strong assessment.guide_item_assessments.first.fitness # => 0.0 to 1.0 ```
Guides whose tiers vary by configuration are built with .with. The six contour melodies are
registered under their own keys, and each key is exactly one such configuration:
ruby
HeadMusic::Style::Guide.get('arch_contour_melody')
# the same guide, spelled out
HeadMusic::Style::Guides::ContourMelody.with(contour: :arch, minimum_melodic_intervals: 2)
Configure it differently and you get a different guide — one the registry does not hold, whose key
is nil and whose display_name falls back to the class. Prefer the key when you mean a registered
guide, and .with when you deliberately want a configuration of your own.
Documentation
- API Documentation: rubydoc.info/gems/head_music
- Contributing Guide: CONTRIBUTING.md
- Changelog: CHANGELOG.md
Requirements
- Ruby 3.3.0 or higher
- ActiveSupport 7.0+
Development
After checking out the repo, run bin/setup to install dependencies.
Running Tests
```bash # Run all tests bundle exec rspec
Run tests with coverage
bundle exec rake
Run quality checks (tests + linting + security)
bundle exec rake quality ```
Code Quality
```bash # Run linting bundle exec rubocop
Run security audit
bundle exec rake bundle:audit:check
Generate documentation
bundle exec rake doc ```
Available Rake Tasks
rake spec- Run testsrake quality- Run tests, linting, and security auditrake doc- Generate YARD documentationrake doc_stats- Show documentation coverage statisticsrake coverage- Open coverage report in browser
Releasing a New Version
- Update the version number in
lib/head_music/version.rb - Commit the version change:
git commit -am "Bump version to X.Y.Z" - Push to main:
git push origin main - Release the gem:
bash
bundle exec rake release
This will:
- Build the gem
- Create and push a git tag (e.g., vX.Y.Z)
- Push the gem to RubyGems
The git tag push also triggers a GitHub Actions workflow that creates a GitHub Release with auto-generated release notes.
Contributing
We welcome contributions! Please see our Contributing Guide for details.
Project Structure
lib/head_music/
├── analysis/ # Musical analysis tools (intervals, chords, etc.)
├── content/ # Musical content (compositions, voices, notes)
├── instruments/ # Instrument definitions and properties
├── rudiment/ # Basic music theory elements (pitches, scales, etc.)
└── style/ # Style analysis and composition rules
Code of Conduct
This project is intended to be a safe, welcoming space for collaboration. Contributors are expected to adhere to our Code of Conduct.
License
The gem is available as open source under the terms of the MIT License.
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Security: For security issues, please email robert.head@gmail.com