head_music 8.3.0 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +9 -3
- data/CHANGELOG.md +71 -0
- data/CLAUDE.md +62 -25
- data/Gemfile +7 -1
- data/Gemfile.lock +91 -3
- data/MUSIC_THEORY.md +120 -0
- data/README.md +18 -0
- data/Rakefile +7 -2
- data/head_music.gemspec +1 -1
- data/lib/head_music/analysis/diatonic_interval.rb +29 -27
- data/lib/head_music/analysis/dyad.rb +229 -0
- data/lib/head_music/analysis/interval_consonance.rb +51 -0
- data/lib/head_music/analysis/melodic_interval.rb +1 -1
- data/lib/head_music/analysis/pitch_class_set.rb +111 -14
- data/lib/head_music/analysis/{pitch_set.rb → pitch_collection.rb} +11 -5
- data/lib/head_music/analysis/sonority.rb +50 -12
- data/lib/head_music/content/note.rb +1 -1
- data/lib/head_music/content/placement.rb +1 -1
- data/lib/head_music/content/position.rb +1 -1
- data/lib/head_music/content/voice.rb +1 -1
- data/lib/head_music/instruments/alternate_tuning.rb +102 -0
- data/lib/head_music/instruments/alternate_tunings.yml +78 -0
- data/lib/head_music/instruments/instrument.rb +231 -72
- data/lib/head_music/instruments/instrument_configuration.rb +66 -0
- data/lib/head_music/instruments/instrument_configuration_option.rb +38 -0
- data/lib/head_music/instruments/instrument_configurations.yml +288 -0
- data/lib/head_music/instruments/instrument_families.yml +77 -0
- data/lib/head_music/instruments/instrument_family.rb +15 -5
- data/lib/head_music/instruments/instruments.yml +795 -965
- data/lib/head_music/instruments/playing_technique.rb +75 -0
- data/lib/head_music/instruments/playing_techniques.yml +826 -0
- data/lib/head_music/instruments/score_order.rb +136 -0
- data/lib/head_music/instruments/score_orders.yml +130 -0
- data/lib/head_music/instruments/staff.rb +61 -1
- data/lib/head_music/instruments/staff_scheme.rb +6 -4
- data/lib/head_music/instruments/stringing.rb +115 -0
- data/lib/head_music/instruments/stringing_course.rb +58 -0
- data/lib/head_music/instruments/stringings.yml +168 -0
- data/lib/head_music/instruments/variant.rb +6 -1
- data/lib/head_music/locales/de.yml +29 -0
- data/lib/head_music/locales/en.yml +106 -0
- data/lib/head_music/locales/es.yml +29 -0
- data/lib/head_music/locales/fr.yml +29 -0
- data/lib/head_music/locales/it.yml +29 -0
- data/lib/head_music/locales/ru.yml +29 -0
- data/lib/head_music/{rudiment → notation}/musical_symbol.rb +3 -3
- data/lib/head_music/notation/staff_mapping.rb +70 -0
- data/lib/head_music/notation/staff_position.rb +62 -0
- data/lib/head_music/notation.rb +7 -0
- data/lib/head_music/rudiment/alteration.rb +34 -49
- data/lib/head_music/rudiment/alterations.yml +32 -0
- data/lib/head_music/rudiment/base.rb +9 -0
- data/lib/head_music/rudiment/chromatic_interval.rb +4 -7
- data/lib/head_music/rudiment/clef.rb +2 -2
- data/lib/head_music/rudiment/consonance.rb +39 -5
- data/lib/head_music/rudiment/diatonic_context.rb +25 -0
- data/lib/head_music/rudiment/key.rb +77 -0
- data/lib/head_music/rudiment/key_signature/enharmonic_equivalence.rb +1 -1
- data/lib/head_music/rudiment/key_signature.rb +21 -8
- data/lib/head_music/rudiment/letter_name.rb +3 -3
- data/lib/head_music/rudiment/meter.rb +19 -9
- data/lib/head_music/rudiment/mode.rb +92 -0
- data/lib/head_music/rudiment/note.rb +112 -0
- data/lib/head_music/rudiment/pitch/parser.rb +52 -0
- data/lib/head_music/rudiment/pitch.rb +5 -6
- data/lib/head_music/rudiment/pitch_class.rb +1 -1
- data/lib/head_music/rudiment/quality.rb +1 -1
- data/lib/head_music/rudiment/reference_pitch.rb +1 -1
- data/lib/head_music/rudiment/register.rb +4 -1
- data/lib/head_music/rudiment/rest.rb +36 -0
- data/lib/head_music/rudiment/rhythmic_element.rb +53 -0
- data/lib/head_music/rudiment/rhythmic_unit/parser.rb +86 -0
- data/lib/head_music/rudiment/rhythmic_unit.rb +13 -5
- data/lib/head_music/rudiment/rhythmic_units.yml +80 -0
- data/lib/head_music/rudiment/rhythmic_value/parser.rb +77 -0
- data/lib/head_music/{content → rudiment}/rhythmic_value.rb +23 -5
- data/lib/head_music/rudiment/scale.rb +4 -5
- data/lib/head_music/rudiment/scale_degree.rb +1 -1
- data/lib/head_music/rudiment/scale_type.rb +9 -3
- data/lib/head_music/rudiment/solmization.rb +1 -1
- data/lib/head_music/rudiment/spelling.rb +8 -4
- data/lib/head_music/rudiment/tempo.rb +85 -0
- data/lib/head_music/rudiment/tonal_context.rb +35 -0
- data/lib/head_music/rudiment/tuning/just_intonation.rb +0 -39
- data/lib/head_music/rudiment/tuning/meantone.rb +0 -39
- data/lib/head_music/rudiment/tuning/pythagorean.rb +0 -39
- data/lib/head_music/rudiment/tuning.rb +21 -1
- data/lib/head_music/rudiment/unpitched_note.rb +62 -0
- data/lib/head_music/style/guidelines/consonant_climax.rb +2 -2
- data/lib/head_music/style/medieval_tradition.rb +26 -0
- data/lib/head_music/style/modern_tradition.rb +31 -0
- data/lib/head_music/style/renaissance_tradition.rb +26 -0
- data/lib/head_music/style/tradition.rb +21 -0
- data/lib/head_music/time/clock_position.rb +84 -0
- data/lib/head_music/time/conductor.rb +264 -0
- data/lib/head_music/time/meter_event.rb +37 -0
- data/lib/head_music/time/meter_map.rb +173 -0
- data/lib/head_music/time/musical_position.rb +188 -0
- data/lib/head_music/time/smpte_timecode.rb +164 -0
- data/lib/head_music/time/tempo_event.rb +40 -0
- data/lib/head_music/time/tempo_map.rb +187 -0
- data/lib/head_music/time.rb +32 -0
- data/lib/head_music/utilities/case.rb +27 -0
- data/lib/head_music/utilities/hash_key.rb +34 -2
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +71 -22
- data/user_stories/active/string-pitches.md +41 -0
- data/user_stories/backlog/notation-style.md +183 -0
- data/user_stories/backlog/organizing-content.md +80 -0
- data/user_stories/done/consonance-dissonance-classification.md +117 -0
- data/user_stories/{backlog → done}/dyad-analysis.md +6 -16
- data/user_stories/done/epic--score-order/PLAN.md +244 -0
- data/user_stories/done/expand-playing-techniques.md +38 -0
- data/user_stories/done/handle-time.md +7 -0
- data/user_stories/done/handle-time.rb +163 -0
- data/user_stories/done/instrument-architecture.md +238 -0
- data/user_stories/done/instrument-variant.md +65 -0
- data/user_stories/done/move-musical-symbol-to-notation.md +161 -0
- data/user_stories/done/move-staff-mapping-to-notation.md +158 -0
- data/user_stories/done/move-staff-position-to-notation.md +141 -0
- data/user_stories/done/notation-module-foundation.md +102 -0
- data/user_stories/done/percussion_set.md +260 -0
- data/user_stories/done/sonority-identification.md +37 -0
- data/user_stories/done/superclass-for-note.md +30 -0
- data/user_stories/epics/notation-module.md +135 -0
- data/user_stories/visioning/agentic-daw.md +2 -0
- metadata +84 -18
- data/TODO.md +0 -109
- data/check_instrument_consistency.rb +0 -0
- data/test_translations.rb +0 -15
- data/user_stories/backlog/consonance-dissonance-classification.md +0 -57
- data/user_stories/backlog/pitch-set-classification.md +0 -62
- data/user_stories/backlog/sonority-identification.md +0 -47
- /data/user_stories/{backlog → done/epic--score-order}/band-score-order.md +0 -0
- /data/user_stories/{backlog → done/epic--score-order}/chamber-ensemble-score-order.md +0 -0
- /data/user_stories/{backlog → done/epic--score-order}/orchestral-score-order.md +0 -0
- /data/user_stories/{backlog → done}/pitch-class-set-analysis.md +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c3abf9e12840eb88aee1cb454116cbd092f224eb1961cfda5567444ad9e91d2f
|
|
4
|
+
data.tar.gz: e7680154e971414788fc8bd1a87f53111bc82c9452f4df5a8ad47a74b1c5c16d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 199c8cdba49e5d6e15e7047a8bb8ee28eaa5c8087c9f427916d087d4ce8986f04cf7dd354666247b389836256230d5c3535ff5fd79964443626e397c0cdb3288
|
|
7
|
+
data.tar.gz: d793d6730a790b088fcc7906bc6cb02b865364852767534c15c1b76b40622ae6a12b23a2bde5f651687e9ce124941501ae8d1f17f9a63a7c8c473a2112914024
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -12,7 +12,8 @@ jobs:
|
|
|
12
12
|
strategy:
|
|
13
13
|
fail-fast: false
|
|
14
14
|
matrix:
|
|
15
|
-
ruby-version: ['3.3
|
|
15
|
+
ruby-version: ['3.3', '3.4']
|
|
16
|
+
activesupport-version: ['7.2', '8.0']
|
|
16
17
|
|
|
17
18
|
steps:
|
|
18
19
|
- uses: actions/checkout@v4
|
|
@@ -21,13 +22,18 @@ jobs:
|
|
|
21
22
|
uses: ruby/setup-ruby@v1
|
|
22
23
|
with:
|
|
23
24
|
ruby-version: ${{ matrix.ruby-version }}
|
|
24
|
-
bundler-cache:
|
|
25
|
+
bundler-cache: false
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies with ActiveSupport ${{ matrix.activesupport-version }}
|
|
28
|
+
run: |
|
|
29
|
+
bundle config set --local path vendor/bundle
|
|
30
|
+
ACTIVESUPPORT_VERSION="${{ matrix.activesupport-version }}" bundle install
|
|
25
31
|
|
|
26
32
|
- name: Run tests
|
|
27
33
|
run: bundle exec rspec
|
|
28
34
|
|
|
29
35
|
- name: Upload coverage to Codecov
|
|
30
|
-
if: matrix.ruby-version == '3.3.0'
|
|
36
|
+
if: matrix.ruby-version == '3.3' && matrix.activesupport-version == '8.0'
|
|
31
37
|
uses: codecov/codecov-action@v5
|
|
32
38
|
with:
|
|
33
39
|
token: ${{ secrets.CODECOV_TOKEN }}
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,77 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [11.0.0] - 2026-01-05
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **BREAKING**: Widened ActiveSupport dependency from `~> 7.0` to `>= 7.0, < 10` to support Rails 8.x
|
|
14
|
+
- Improved I18n initialization to be non-destructive:
|
|
15
|
+
- No longer overwrites `I18n.default_locale` (allows Rails apps to control their default)
|
|
16
|
+
- Adds HeadMusic locales to `available_locales` instead of replacing them
|
|
17
|
+
- Only sets fallbacks if not already configured by the application
|
|
18
|
+
- Updated CI workflow to test against both ActiveSupport 7.x and 8.x
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Fixed compatibility issue with Rails 8.1.x applications
|
|
22
|
+
|
|
23
|
+
## [10.0.0] - 2025-12-01
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Internal release for testing
|
|
27
|
+
|
|
28
|
+
## [9.0.0] - 2025-10-24
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- Added `HeadMusic::Rudiment::Pitch::Parser` for strict pitch parsing
|
|
32
|
+
- Added `HeadMusic::Rudiment::RhythmicValue::Parser` for rhythmic value parsing
|
|
33
|
+
- Both parsers provide standardized `.parse()` class method API
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- `Pitch.from_name` now uses `Pitch::Parser` internally
|
|
37
|
+
- `RhythmicValue.get` now uses `RhythmicValue::Parser` internally
|
|
38
|
+
- `Note.get` now parses "pitch rhythmic_value" strings inline without Parse module
|
|
39
|
+
|
|
40
|
+
### Removed
|
|
41
|
+
- **BREAKING**: Removed `HeadMusic::Parse::Pitch` class
|
|
42
|
+
- **BREAKING**: Removed `HeadMusic::Parse::RhythmicValue` class
|
|
43
|
+
- **BREAKING**: Removed `HeadMusic::Parse::RhythmicElement` class
|
|
44
|
+
- **BREAKING**: Removed entire `HeadMusic::Parse` module
|
|
45
|
+
|
|
46
|
+
### Migration Guide
|
|
47
|
+
|
|
48
|
+
If you were using the removed Parse classes, migrate as follows:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
# Before (v8.x)
|
|
52
|
+
parser = HeadMusic::Parse::Pitch.new("C#4")
|
|
53
|
+
pitch = parser.pitch
|
|
54
|
+
|
|
55
|
+
# After (v9.x)
|
|
56
|
+
pitch = HeadMusic::Rudiment::Pitch.get("C#4")
|
|
57
|
+
# or for strict parsing:
|
|
58
|
+
pitch = HeadMusic::Rudiment::Pitch::Parser.parse("C#4")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
# Before (v8.x)
|
|
63
|
+
parser = HeadMusic::Parse::RhythmicValue.new("dotted quarter")
|
|
64
|
+
value = parser.rhythmic_value
|
|
65
|
+
|
|
66
|
+
# After (v9.x)
|
|
67
|
+
value = HeadMusic::Rudiment::RhythmicValue.get("dotted quarter")
|
|
68
|
+
# or for strict parsing:
|
|
69
|
+
value = HeadMusic::Rudiment::RhythmicValue::Parser.parse("dotted quarter")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
# Before (v8.x)
|
|
74
|
+
parser = HeadMusic::Parse::RhythmicElement.new("F#4 dotted-quarter")
|
|
75
|
+
note = parser.note
|
|
76
|
+
|
|
77
|
+
# After (v9.x)
|
|
78
|
+
note = HeadMusic::Rudiment::Note.get("F#4 dotted-quarter")
|
|
79
|
+
```
|
|
80
|
+
|
|
10
81
|
## [8.2.1] - 2025-06-21
|
|
11
82
|
|
|
12
83
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -41,14 +41,26 @@ bundle exec rake doc
|
|
|
41
41
|
bundle exec rake doc_stats
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
###
|
|
44
|
+
### Git Etiquette
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
**IMPORTANT: Do not make a commit unless I explicitly ask you to.** Wait for explicit instruction before running `git commit`.
|
|
47
|
+
|
|
48
|
+
When composing git commit messages, follow best-practices. However, do not mention yourself (claude) or list yourself as a co-author.
|
|
49
|
+
|
|
50
|
+
This project uses a rebase flow and `main` as the mainline branch.
|
|
51
|
+
|
|
52
|
+
### Code Style, Linting, and Formatting
|
|
53
|
+
|
|
54
|
+
The project uses Standard Ruby for style enforcement. Always run linting after editing and before committing.
|
|
47
55
|
|
|
48
56
|
```bash
|
|
49
|
-
bundle exec rubocop
|
|
57
|
+
bundle exec rubocop -a
|
|
50
58
|
```
|
|
51
59
|
|
|
60
|
+
Always strip trailing whitespace from all lines being added or edited. Always include a blank line at the end of each file.
|
|
61
|
+
|
|
62
|
+
Do not use an assignment inside a condition.
|
|
63
|
+
|
|
52
64
|
### Testing
|
|
53
65
|
|
|
54
66
|
Tests are written in RSpec and located in the `/spec` directory, mirroring the `/lib` structure. The project requires 90% code coverage minimum.
|
|
@@ -60,25 +72,38 @@ Tests are written in RSpec and located in the `/spec` directory, mirroring the `
|
|
|
60
72
|
The codebase follows a domain-driven design with clear module boundaries:
|
|
61
73
|
|
|
62
74
|
1. **HeadMusic::Rudiment** - Core music theory elements
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
- Abstract concepts: pitch, interval, scale, chord, key
|
|
76
|
+
- Duration concepts (without visual representation)
|
|
77
|
+
- Factory methods: `.get()` for most rudiments
|
|
78
|
+
|
|
79
|
+
2. **HeadMusic::Notation** - Visual music notation and representation
|
|
80
|
+
- Staff positions, lines, spaces, ledger lines
|
|
81
|
+
- Musical symbols (ASCII, Unicode, HTML entities)
|
|
82
|
+
- Clef placement and rendering
|
|
83
|
+
- Notehead shapes, stems, flags, beams
|
|
84
|
+
- Accidental placement rules
|
|
85
|
+
- Future: ties, slurs, articulations, dynamics
|
|
86
|
+
|
|
87
|
+
3. **HeadMusic::Instruments** - Instrument definitions
|
|
88
|
+
- Instrument families and classification
|
|
89
|
+
- Pitch ranges and transposition
|
|
90
|
+
- Playing techniques
|
|
91
|
+
- Score ordering
|
|
92
|
+
|
|
93
|
+
4. **HeadMusic::Content** - Musical composition representation
|
|
94
|
+
- Compositions, voices, bars, positions
|
|
95
|
+
- Notes in context (pitch + duration + placement)
|
|
96
|
+
- Temporal organization
|
|
97
|
+
|
|
98
|
+
5. **HeadMusic::Analysis** - Musical analysis tools
|
|
99
|
+
- Intervals, chords, motion analysis
|
|
100
|
+
- Harmonic and melodic analysis
|
|
101
|
+
- Pitch class sets and collections
|
|
102
|
+
|
|
103
|
+
6. **HeadMusic::Style** - Composition rules and guidelines
|
|
104
|
+
- Counterpoint rules
|
|
105
|
+
- Voice leading guidelines
|
|
106
|
+
- Style analysis
|
|
82
107
|
|
|
83
108
|
### Key Design Patterns
|
|
84
109
|
|
|
@@ -109,12 +134,21 @@ The gem supports multiple languages through the HeadMusic::Named mixin:
|
|
|
109
134
|
- Shared examples in `spec/support/`
|
|
110
135
|
- `composition_context.rb` provides test utilities
|
|
111
136
|
|
|
137
|
+
### Documentation Philosophy
|
|
138
|
+
|
|
139
|
+
This project deliberately deprioritizes formal documentation in favor of clear, comprehensive tests.
|
|
140
|
+
|
|
141
|
+
- **Tests serve as documentation**: RSpec specs demonstrate how to use the code
|
|
142
|
+
- **Comments explain "why", not "what"**: Only add comments when the implementation is surprising or non-obvious
|
|
143
|
+
- **No YARD documentation required**: The code should be self-explanatory through clear naming and test examples
|
|
144
|
+
- Documentation tools like `rake doc` and `rake doc_stats` exist but low coverage is intentional
|
|
145
|
+
|
|
112
146
|
### Code Style
|
|
113
147
|
|
|
114
148
|
- Ruby 3.3.0+ features are allowed
|
|
115
149
|
- Follow Standard Ruby style guide
|
|
116
|
-
- Use YARD documentation format for public methods
|
|
117
150
|
- Prefer delegation over inheritance
|
|
151
|
+
- Always run `bundle exec rubocop -a` after editing ruby code
|
|
118
152
|
|
|
119
153
|
## Common Development Tasks
|
|
120
154
|
|
|
@@ -130,5 +164,8 @@ The gem supports multiple languages through the HeadMusic::Named mixin:
|
|
|
130
164
|
|
|
131
165
|
1. Check for dependent classes that might be affected
|
|
132
166
|
2. Run tests for the specific module: `bundle exec rspec spec/head_music/[module_name]`
|
|
133
|
-
3.
|
|
134
|
-
|
|
167
|
+
3. Ensure translations are updated if names change
|
|
168
|
+
|
|
169
|
+
## Music theory and concepts
|
|
170
|
+
|
|
171
|
+
Please refer to MUSIC_THEORY.md for music theory domain knowledge.
|
data/Gemfile
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
source "https://rubygems.org"
|
|
2
2
|
|
|
3
|
-
ruby "3.3.0"
|
|
3
|
+
ruby ">= 3.3.0"
|
|
4
4
|
|
|
5
5
|
# Specify your gem's dependencies in head_music.gemspec
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
|
+
# Allow CI to test against specific ActiveSupport versions
|
|
9
|
+
if ENV["ACTIVESUPPORT_VERSION"]
|
|
10
|
+
gem "activesupport", "~> #{ENV["ACTIVESUPPORT_VERSION"]}.0"
|
|
11
|
+
end
|
|
12
|
+
|
|
8
13
|
gem "standard", require: false
|
|
9
14
|
|
|
10
15
|
group :test do
|
|
@@ -16,6 +21,7 @@ end
|
|
|
16
21
|
|
|
17
22
|
group :development do
|
|
18
23
|
gem "bundler-audit", require: false
|
|
24
|
+
gem "rubycritic", require: false
|
|
19
25
|
gem "yard", require: false
|
|
20
26
|
gem "kramdown", require: false
|
|
21
27
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
head_music (
|
|
5
|
-
activesupport (
|
|
4
|
+
head_music (11.0.0)
|
|
5
|
+
activesupport (>= 7.0, < 10)
|
|
6
6
|
humanize (~> 2.0)
|
|
7
7
|
i18n (~> 1.8)
|
|
8
8
|
|
|
@@ -21,25 +21,81 @@ GEM
|
|
|
21
21
|
minitest (>= 5.1)
|
|
22
22
|
securerandom (>= 0.3)
|
|
23
23
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
24
|
+
addressable (2.8.7)
|
|
25
|
+
public_suffix (>= 2.0.2, < 7.0)
|
|
24
26
|
ast (2.4.3)
|
|
27
|
+
axiom-types (0.1.1)
|
|
28
|
+
descendants_tracker (~> 0.0.4)
|
|
29
|
+
ice_nine (~> 0.11.0)
|
|
30
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
|
25
31
|
base64 (0.3.0)
|
|
26
32
|
benchmark (0.4.1)
|
|
27
33
|
bigdecimal (3.2.2)
|
|
28
34
|
bundler-audit (0.9.2)
|
|
29
35
|
bundler (>= 1.2.0, < 3)
|
|
30
36
|
thor (~> 1.0)
|
|
37
|
+
childprocess (5.1.0)
|
|
38
|
+
logger (~> 1.5)
|
|
39
|
+
coercible (1.0.0)
|
|
40
|
+
descendants_tracker (~> 0.0.1)
|
|
31
41
|
concurrent-ruby (1.3.5)
|
|
32
42
|
connection_pool (2.5.3)
|
|
43
|
+
descendants_tracker (0.0.4)
|
|
44
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
|
33
45
|
diff-lcs (1.6.2)
|
|
34
46
|
docile (1.4.1)
|
|
35
47
|
drb (2.2.3)
|
|
48
|
+
dry-configurable (1.3.0)
|
|
49
|
+
dry-core (~> 1.1)
|
|
50
|
+
zeitwerk (~> 2.6)
|
|
51
|
+
dry-core (1.1.0)
|
|
52
|
+
concurrent-ruby (~> 1.0)
|
|
53
|
+
logger
|
|
54
|
+
zeitwerk (~> 2.6)
|
|
55
|
+
dry-inflector (1.2.0)
|
|
56
|
+
dry-initializer (3.2.0)
|
|
57
|
+
dry-logic (1.6.0)
|
|
58
|
+
bigdecimal
|
|
59
|
+
concurrent-ruby (~> 1.0)
|
|
60
|
+
dry-core (~> 1.1)
|
|
61
|
+
zeitwerk (~> 2.6)
|
|
62
|
+
dry-schema (1.14.1)
|
|
63
|
+
concurrent-ruby (~> 1.0)
|
|
64
|
+
dry-configurable (~> 1.0, >= 1.0.1)
|
|
65
|
+
dry-core (~> 1.1)
|
|
66
|
+
dry-initializer (~> 3.2)
|
|
67
|
+
dry-logic (~> 1.5)
|
|
68
|
+
dry-types (~> 1.8)
|
|
69
|
+
zeitwerk (~> 2.6)
|
|
70
|
+
dry-types (1.8.3)
|
|
71
|
+
bigdecimal (~> 3.0)
|
|
72
|
+
concurrent-ruby (~> 1.0)
|
|
73
|
+
dry-core (~> 1.0)
|
|
74
|
+
dry-inflector (~> 1.0)
|
|
75
|
+
dry-logic (~> 1.4)
|
|
76
|
+
zeitwerk (~> 2.6)
|
|
77
|
+
erubi (1.13.1)
|
|
78
|
+
flay (2.13.3)
|
|
79
|
+
erubi (~> 1.10)
|
|
80
|
+
path_expander (~> 1.0)
|
|
81
|
+
ruby_parser (~> 3.0)
|
|
82
|
+
sexp_processor (~> 4.0)
|
|
83
|
+
flog (4.8.0)
|
|
84
|
+
path_expander (~> 1.0)
|
|
85
|
+
ruby_parser (~> 3.1, > 3.1.0)
|
|
86
|
+
sexp_processor (~> 4.8)
|
|
36
87
|
humanize (2.5.1)
|
|
37
88
|
i18n (1.14.7)
|
|
38
89
|
concurrent-ruby (~> 1.0)
|
|
90
|
+
ice_nine (0.11.2)
|
|
39
91
|
json (2.13.1)
|
|
40
92
|
kramdown (2.5.1)
|
|
41
93
|
rexml (>= 3.3.9)
|
|
42
94
|
language_server-protocol (3.17.0.5)
|
|
95
|
+
launchy (3.1.1)
|
|
96
|
+
addressable (~> 2.8)
|
|
97
|
+
childprocess (~> 5.0)
|
|
98
|
+
logger (~> 1.6)
|
|
43
99
|
lint_roller (1.1.0)
|
|
44
100
|
logger (1.7.0)
|
|
45
101
|
minitest (5.25.5)
|
|
@@ -47,12 +103,20 @@ GEM
|
|
|
47
103
|
parser (3.3.9.0)
|
|
48
104
|
ast (~> 2.4.1)
|
|
49
105
|
racc
|
|
106
|
+
path_expander (1.1.3)
|
|
50
107
|
prism (1.4.0)
|
|
108
|
+
public_suffix (6.0.2)
|
|
51
109
|
racc (1.8.1)
|
|
52
110
|
rainbow (3.1.1)
|
|
53
111
|
rake (13.3.0)
|
|
112
|
+
reek (6.5.0)
|
|
113
|
+
dry-schema (~> 1.13)
|
|
114
|
+
logger (~> 1.6)
|
|
115
|
+
parser (~> 3.3.0)
|
|
116
|
+
rainbow (>= 2.0, < 4.0)
|
|
117
|
+
rexml (~> 3.1)
|
|
54
118
|
regexp_parser (2.10.0)
|
|
55
|
-
rexml (3.4.
|
|
119
|
+
rexml (3.4.4)
|
|
56
120
|
rspec (3.13.1)
|
|
57
121
|
rspec-core (~> 3.13.0)
|
|
58
122
|
rspec-expectations (~> 3.13.0)
|
|
@@ -94,7 +158,23 @@ GEM
|
|
|
94
158
|
lint_roller (~> 1.1)
|
|
95
159
|
rubocop (~> 1.72, >= 1.72.1)
|
|
96
160
|
ruby-progressbar (1.13.0)
|
|
161
|
+
ruby_parser (3.21.1)
|
|
162
|
+
racc (~> 1.5)
|
|
163
|
+
sexp_processor (~> 4.16)
|
|
164
|
+
rubycritic (4.11.0)
|
|
165
|
+
flay (~> 2.13)
|
|
166
|
+
flog (~> 4.7)
|
|
167
|
+
launchy (>= 2.5.2)
|
|
168
|
+
parser (>= 3.3.0.5)
|
|
169
|
+
rainbow (~> 3.1.1)
|
|
170
|
+
reek (~> 6.5.0, < 7.0)
|
|
171
|
+
rexml
|
|
172
|
+
ruby_parser (~> 3.21)
|
|
173
|
+
simplecov (>= 0.22.0)
|
|
174
|
+
tty-which (~> 0.5.0)
|
|
175
|
+
virtus (~> 2.0)
|
|
97
176
|
securerandom (0.4.1)
|
|
177
|
+
sexp_processor (4.17.4)
|
|
98
178
|
simplecov (0.22.0)
|
|
99
179
|
docile (~> 1.1)
|
|
100
180
|
simplecov-html (~> 0.11)
|
|
@@ -114,12 +194,19 @@ GEM
|
|
|
114
194
|
lint_roller (~> 1.1)
|
|
115
195
|
rubocop-performance (~> 1.25.0)
|
|
116
196
|
thor (1.4.0)
|
|
197
|
+
thread_safe (0.3.6)
|
|
198
|
+
tty-which (0.5.0)
|
|
117
199
|
tzinfo (2.0.6)
|
|
118
200
|
concurrent-ruby (~> 1.0)
|
|
119
201
|
unicode-display_width (3.1.4)
|
|
120
202
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
|
121
203
|
unicode-emoji (4.0.4)
|
|
204
|
+
virtus (2.0.0)
|
|
205
|
+
axiom-types (~> 0.1)
|
|
206
|
+
coercible (~> 1.0)
|
|
207
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
|
122
208
|
yard (0.9.37)
|
|
209
|
+
zeitwerk (2.7.3)
|
|
123
210
|
|
|
124
211
|
PLATFORMS
|
|
125
212
|
arm64-darwin-22
|
|
@@ -136,6 +223,7 @@ DEPENDENCIES
|
|
|
136
223
|
rubocop
|
|
137
224
|
rubocop-rake
|
|
138
225
|
rubocop-rspec
|
|
226
|
+
rubycritic
|
|
139
227
|
simplecov
|
|
140
228
|
standard
|
|
141
229
|
yard
|
data/MUSIC_THEORY.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Music Theory
|
|
2
|
+
Domain-specific knowledge for this project.
|
|
3
|
+
|
|
4
|
+
## Rudiments
|
|
5
|
+
|
|
6
|
+
The rudiments of music theory are built up piece-by-piece.
|
|
7
|
+
|
|
8
|
+
letter name
|
|
9
|
+
- C, D, E, F, G, A, B, …
|
|
10
|
+
- equivalent to:
|
|
11
|
+
- do, re, mi, fa, sol, la, si/ti, …
|
|
12
|
+
|
|
13
|
+
alteration
|
|
14
|
+
- sharp, flat, double sharp, double flat
|
|
15
|
+
- optional
|
|
16
|
+
- none is same as 'natural'
|
|
17
|
+
- found in key signatures
|
|
18
|
+
- found in bars as accidentals
|
|
19
|
+
|
|
20
|
+
spelling
|
|
21
|
+
- letter name + alteration
|
|
22
|
+
- example: "E♭"
|
|
23
|
+
|
|
24
|
+
register
|
|
25
|
+
- an integer typically between 0 and 8
|
|
26
|
+
- represents the octaves of an 88-key piano
|
|
27
|
+
- scientific pitch notation
|
|
28
|
+
- middle C is C4
|
|
29
|
+
- increments between B and C
|
|
30
|
+
- a half step below C4 is B3.
|
|
31
|
+
|
|
32
|
+
pitch
|
|
33
|
+
- spelling + register
|
|
34
|
+
- example: "E♭3"
|
|
35
|
+
|
|
36
|
+
rhythmic unit
|
|
37
|
+
- duration
|
|
38
|
+
- expressed in fractions (or multiples) of a standard whole note
|
|
39
|
+
- whole, half, quarter, eighth, sixteenth, …
|
|
40
|
+
- breve, …
|
|
41
|
+
|
|
42
|
+
rhythmic value (duration)
|
|
43
|
+
- a rhythmic unit plus optional augmentation dots
|
|
44
|
+
- augmentation dots extend the duration of the rhythmic unit
|
|
45
|
+
- one dot extends the rhythmic value 50%
|
|
46
|
+
- two dots extend the rhythmic value 75%
|
|
47
|
+
- two dots extend the rhythmic value 87.5%
|
|
48
|
+
|
|
49
|
+
rest
|
|
50
|
+
- a rhythmic value that passes in silence
|
|
51
|
+
|
|
52
|
+
note
|
|
53
|
+
- pitch + rhythmic value
|
|
54
|
+
- example: "E♭3 dotted quarter"
|
|
55
|
+
|
|
56
|
+
unpitched note
|
|
57
|
+
- an unpitched percussion note, such as a drum hit
|
|
58
|
+
- a sounded rhythmic value without a specific pitch
|
|
59
|
+
|
|
60
|
+
tied duration
|
|
61
|
+
- combines multiple rhythmic values into one longer note
|
|
62
|
+
- ties allow rhythmic values to be combined across barlines or strong beats
|
|
63
|
+
|
|
64
|
+
articulation
|
|
65
|
+
- a category of expressions that modify how one or more notes are performed.
|
|
66
|
+
- level of connection
|
|
67
|
+
- staccatissimo - the most staccato, even shorted and more clipped
|
|
68
|
+
- staccato – detatched (shortened to lease space between adjacent notes)
|
|
69
|
+
- tenuto - held of the full value
|
|
70
|
+
- legato - smoothly connected
|
|
71
|
+
- emphasis
|
|
72
|
+
- accent - play with emphasis or a stronger attack
|
|
73
|
+
- marcato - markedly emphasized
|
|
74
|
+
- instrument specific
|
|
75
|
+
- bowings
|
|
76
|
+
- breath mark
|
|
77
|
+
- roll mark
|
|
78
|
+
|
|
79
|
+
## Instrument Families
|
|
80
|
+
|
|
81
|
+
There are several ways that people talk about families and other categorizations of instruments. For example, the "string family" or "woodwind family", but also more specifically, the word family is applied more specifically (e.g. the "oboe family").
|
|
82
|
+
|
|
83
|
+
### Text Conversation with Brian Head (May 7, 2023)
|
|
84
|
+
|
|
85
|
+
Brian Head, brother and faculty at USC Thornton School of Music
|
|
86
|
+
https://music.usc.edu/brian-head/
|
|
87
|
+
|
|
88
|
+
Robert Head:
|
|
89
|
+
Hey, Brian. You have a sec for a music question? I’m trying to come up with different terms for the way “family” is used. “Woodwind family” vs. “Oboe family”.
|
|
90
|
+
|
|
91
|
+
Brian Head:
|
|
92
|
+
Those are good, I’d say. In what context?
|
|
93
|
+
|
|
94
|
+
Robert Head:
|
|
95
|
+
In my software project, I’m trying to define those relationships. One is a section of the orchestra and the other is species of instrument. But usually people, in my experience, just say “family”.
|
|
96
|
+
Is there some more precise terminology or adjective that can disambiguate those two terms?
|
|
97
|
+
|
|
98
|
+
Brian Head:
|
|
99
|
+
Hmmm. Already “family” is informal. Blatter distinguishes between “choir” and “family” as in the trumpet family” within the “brass choir”.
|
|
100
|
+
|
|
101
|
+
Robert Head:
|
|
102
|
+
Ah, yes! I do like that.
|
|
103
|
+
But does it apply to percussion?
|
|
104
|
+
“Section” is another candidate, but that gets used for string parts as well.
|
|
105
|
+
|
|
106
|
+
Brian Head:
|
|
107
|
+
Strings and percussion probably don’t think of themselves as a choir, but if you’re mainly looking for a taxonomically consistent word, that’s the feat I can think of at the moment.
|
|
108
|
+
Section is a good word, too, which easily flows between smaller and larger meanings.
|
|
109
|
+
|
|
110
|
+
Robert Head:
|
|
111
|
+
Obviously, the four “families” is a garbage way to classify all instruments and it really is more applicable to the orchestral context, so maybe “orchestra family” or “orchestra section”.
|
|
112
|
+
|
|
113
|
+
Brian Head:
|
|
114
|
+
I’d say that “family” is best used at the instrument level, as in “saxophone family”. Choir or section are better for larger collections. Still, both of those words connote membership in an orchestra. Or large ensemble. “Woodwinds” or “percussion” describe the class of instruments themselves.
|
|
115
|
+
|
|
116
|
+
Robert Head:
|
|
117
|
+
Cool
|
|
118
|
+
|
|
119
|
+
Action Item: Call them orchestra_section
|
|
120
|
+
DONE
|
data/README.md
CHANGED
|
@@ -101,6 +101,24 @@ bundle exec rake doc
|
|
|
101
101
|
- `rake doc_stats` - Show documentation coverage statistics
|
|
102
102
|
- `rake coverage` - Open coverage report in browser
|
|
103
103
|
|
|
104
|
+
### Releasing a New Version
|
|
105
|
+
|
|
106
|
+
1. Update the version number in `lib/head_music/version.rb`
|
|
107
|
+
2. Commit the version change: `git commit -am "Bump version to X.Y.Z"`
|
|
108
|
+
3. Push to main: `git push origin main`
|
|
109
|
+
4. Release the gem:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
bundle exec rake release
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This will:
|
|
116
|
+
- Build the gem
|
|
117
|
+
- Create and push a git tag (e.g., `vX.Y.Z`)
|
|
118
|
+
- Push the gem to RubyGems
|
|
119
|
+
|
|
120
|
+
The git tag push also triggers a GitHub Actions workflow that creates a GitHub Release with auto-generated release notes.
|
|
121
|
+
|
|
104
122
|
## Contributing
|
|
105
123
|
|
|
106
124
|
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
data/Rakefile
CHANGED
|
@@ -26,10 +26,15 @@ rescue LoadError
|
|
|
26
26
|
# bundler-audit not available
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
desc "Run RubyCritic code quality analysis"
|
|
30
|
+
task :rubycritic do
|
|
31
|
+
sh "rubycritic lib"
|
|
32
|
+
end
|
|
33
|
+
|
|
29
34
|
task default: :spec
|
|
30
35
|
|
|
31
|
-
desc "Run all quality checks (tests, linting, security audit)"
|
|
32
|
-
task quality: [:spec, :standard, "bundle:audit:check"]
|
|
36
|
+
desc "Run all quality checks (tests, linting, security audit, code quality)"
|
|
37
|
+
task quality: [:spec, :standard, "bundle:audit:check", :rubycritic]
|
|
33
38
|
|
|
34
39
|
desc "Open an irb session preloaded with this library"
|
|
35
40
|
task :console do
|
data/head_music.gemspec
CHANGED
|
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
|
32
32
|
|
|
33
33
|
spec.required_ruby_version = ">= 3.3.0"
|
|
34
34
|
|
|
35
|
-
spec.add_runtime_dependency "activesupport", "
|
|
35
|
+
spec.add_runtime_dependency "activesupport", ">= 7.0", "< 10"
|
|
36
36
|
spec.add_runtime_dependency "humanize", "~> 2.0"
|
|
37
37
|
spec.add_runtime_dependency "i18n", "~> 1.8"
|
|
38
38
|
|