-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
176 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,20 @@ | ||
module MusicTheory | ||
|
||
# Write your package code here. | ||
export Pitch, Note, Accidental, NoteClass, Interval, IntervalType, | ||
MajorScale, MinorScale, Major, Minor, Perfect, Augmented, Diminished, | ||
𝄫, ♭, ♮, ♯, 𝄪, C, D, E, F, G, A, B, C♮, D♮, E♮, F♮, G♮, A♮, B♮, C♯, D♯, E♯, F♯, G♯, A♯, | ||
B♯, C♭, D♭, E♭, F♭, G♭, A♭, B♭, C𝄫, D𝄫, E𝄫, F𝄫, G𝄫, A𝄫, B𝄫, C𝄪, D𝄪, E𝄪, F𝄪, G, | ||
note_names | ||
|
||
|
||
# export all identifiers in this module: | ||
for n in names(@__MODULE__; all=true)♮ | ||
if Base.isidentifier(n) && n ∉ (Symbol(@__MODULE__), :eval, :include) | ||
@eval export $n | ||
end | ||
end | ||
|
||
include("notes.jl") | ||
include("intervals.jl") | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
@enum IntervalQuality Perfect Augmented Diminished Major Minor | ||
|
||
# distance is 0 for unison, 1 for second, etc. | ||
# so is 7 for octave, 8 for ninth, etc. | ||
struct Interval | ||
distance::Int | ||
quality::IntervalQuality | ||
end | ||
|
||
function interval_name(distance::Int) | ||
if distance == 0 | ||
return "unison" | ||
elseif distance == 1 | ||
return "2nd" | ||
elseif distance == 2 | ||
return "3rd" | ||
elseif distance == 7 | ||
return "octave" | ||
else | ||
return string(distance + 1, "th") | ||
end | ||
end | ||
|
||
Base.show(io::IO, interval::Interval) = | ||
print(io, interval.quality, " ", interval_name(interval.distance)) | ||
|
||
|
||
interval_semitones = | ||
Dict(0 => 0, 1 => 2, 2 => 4, 3 => 5, 4 => 7, 5 => 9, 6 => 11) | ||
|
||
interval_quality_semitones = Dict( | ||
Perfect => 0, | ||
Augmented => +1, | ||
Diminished => -1, | ||
Major => 0, | ||
Minor => -1 | ||
) | ||
|
||
semitone(interval::Interval) = | ||
interval_semitones[interval.distance] + interval_quality_semitones[interval.quality] | ||
|
||
Base.:(<=)(n1::Pitch, n2::Pitch) = M.semitone(n1) <= M.semitone(n2) | ||
|
||
|
||
|
||
# interval between two pitches: | ||
function interval(n1::Pitch, n2::Pitch) | ||
if n2 <= n1 | ||
n1, n2 = n2, n1 | ||
end | ||
|
||
note_distance = Int(n2.note.noteclass) - Int(n1.note.noteclass) | ||
|
||
octave_distance = 7 * (n2.octave - n1.octave) | ||
|
||
total_note_distance = note_distance + octave_distance | ||
|
||
semitone_distance = (M.semitone(n2) - M.semitone(n1)) % 12 | ||
|
||
base_interval_semitone = interval_semitones[total_note_distance %7] | ||
alteration_distance = semitone_distance - base_interval_semitone | ||
|
||
|
||
|
||
@show note_distance, total_note_distance, semitone_distance, base_interval_semitone, alteration_distance | ||
|
||
if abs(note_distance) + 1 ∈ (1, 4, 5) | ||
if alteration_distance == 0 | ||
return Interval(total_note_distance, Perfect) | ||
elseif alteration_distance >= 1 | ||
return Interval(total_note_distance, Augmented) | ||
elseif alteration_distance <= -1 | ||
return Interval(total_note_distance, Diminished) | ||
end | ||
else | ||
if alteration_distance == 0 | ||
return Interval(total_note_distance, Major) | ||
elseif alteration_distance == -1 | ||
return Interval(total_note_distance, Minor) | ||
elseif alteration_distance > 0 | ||
return Interval(total_note_distance, Augmented) | ||
elseif alteration_distance < -1 | ||
return Interval(total_note_distance, Diminished) | ||
end | ||
end | ||
end | ||
|
||
|
||
interval(M.C4, M.C6) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
|
||
## Names and semitone mappings | ||
@enum NoteClass C=0 D E F G A B | ||
|
||
const note_names = instances(NoteClass) | ||
const scale_grades = Dict(name => value - 1 for (value, name) in enumerate(note_names)) | ||
|
||
# mappings from note names to semitones: | ||
const note_semitones = Dict(C => 0, D => 2, E => 4, F => 5, G => 7, A => 9, B => 11) | ||
const semitone_to_note = Dict(v => k for (k, v) in note_semitones) | ||
|
||
|
||
## Accidentals | ||
@enum Accidental 𝄫 ♭ ♮ ♯ 𝄪 | ||
const accidental_semitones = Dict(𝄫 => -2, ♭ => -1, ♮ => 0, ♯ => 1, 𝄪 => 2) | ||
const semitone_to_accidental = Dict(v => k for (k, v) in accidental_semitones) | ||
|
||
|
||
## Notes | ||
struct Note | ||
noteclass::NoteClass | ||
accidental::Accidental | ||
end | ||
|
||
# default is natural: | ||
Note(noteclass::NoteClass) = Note(noteclass, ♮) | ||
Base.convert(::Type{Note}, noteclass::NoteClass) = Note(noteclass, ♮) | ||
|
||
Base.show(io::IO, note::Note) = print(io, note.noteclass, note.accidental) | ||
|
||
"Scientific pitch notation, e.g. C4" | ||
struct Pitch | ||
note::Note | ||
octave::Int | ||
end | ||
|
||
function Base.show(io::IO, pitch::Pitch) | ||
if pitch.note.accidental == ♮ | ||
print(io, pitch.note.noteclass, pitch.octave) | ||
else | ||
print(io, pitch.note, pitch.octave) | ||
end | ||
end | ||
|
||
Note(C, ♮) | ||
|
||
Base.:*(note::NoteClass, accidental::Accidental) = Note(note, accidental) | ||
|
||
for note in instances(NoteClass), accidental in instances(Accidental) | ||
name = Symbol(note, accidental) | ||
|
||
@eval $(name) = $(note) * $(accidental) | ||
end | ||
|
||
|
||
for note in instances(NoteClass), octave in 0:8 | ||
name = Symbol(note, octave) | ||
@eval $(name) = Pitch($(note), $(octave)) | ||
|
||
for accidental in instances(Accidental) | ||
name = Symbol(note, accidental, octave) | ||
@eval $(name) = Pitch(Note($(note), $(accidental)), $(octave)) | ||
end | ||
end | ||
|
||
octave(pitch::Pitch) = pitch.octave | ||
|
||
const middle_C = C4 |