diff --git a/src/MusicTheory.jl b/src/MusicTheory.jl index c6804b9..aaea273 100644 --- a/src/MusicTheory.jl +++ b/src/MusicTheory.jl @@ -12,6 +12,8 @@ export Interval, IntervalType, Major, Minor, Perfect, Augmented, Diminished, export Major_2nd, Minor_2nd, Major_3rd, Minor_3rd, Perfect_4th, Perfect_5th, Major_6th, Minor_6th, Major_7th, Minor_7th, Octave +export major_scale, natural_minor_scale, melodic_minor_scale, harmonic_minor_scale + include("notes.jl") include("intervals.jl") include("scales.jl") diff --git a/src/intervals.jl b/src/intervals.jl index 0b5b0e1..737e3dd 100644 --- a/src/intervals.jl +++ b/src/intervals.jl @@ -43,7 +43,7 @@ interval_quality_semitones = Dict( semitone(interval::Interval) = interval_semitones[interval.distance] + interval_quality_semitones[interval.quality] -Base.:(<=)(n1::Pitch, n2::Pitch) = M.semitone(n1) <= M.semitone(n2) +Base.:(<=)(n1::Pitch, n2::Pitch) = semitone(n1) <= semitone(n2) @@ -59,7 +59,7 @@ function interval(n1::Pitch, n2::Pitch) total_note_distance = note_distance + octave_distance - semitone_distance = (M.semitone(n2) - M.semitone(n1)) % 12 + semitone_distance = (semitone(n2) - semitone(n1)) % 12 base_interval_semitone = interval_semitones[total_note_distance %7] alteration_distance = semitone_distance - base_interval_semitone @@ -95,21 +95,31 @@ tone(interval::Interval) = interval.distance semitone(interval::Interval) = interval_semitones[interval.distance] + interval_quality_semitones[interval.quality] -function add_interval(p::Pitch, interval::Interval) - new_tone = tone(p) + tone(interval) +function add_interval(n::Note, interval::Interval) + new_tone = (tone(n) + tone(interval)) % 7 - new_note_class = NoteClass(new_tone % 7) + new_note_class = NoteClass(new_tone) new_octave = new_tone ÷ 7 - new_semitone = semitone(p) + semitone(interval) + new_semitone = semitone(n) + semitone(interval) new_note = find_accidental(new_semitone % 12, new_note_class) + return new_note +end + + +function add_interval(p::Pitch, interval::Interval) + new_tone = tone(p) + tone(interval) + new_octave = new_tone ÷ 7 + + new_note = add_interval(p.note, interval) new_pitch = Pitch(new_note, new_octave) return new_pitch end Base.:(+)(p::Pitch, interval::Interval) = add_interval(p, interval) +Base.:(+)(n::Note, interval::Interval) = add_interval(n, interval) const Minor_2nd = Interval(1, Minor) const Major_2nd = Interval(1, Major) diff --git a/src/notes.jl b/src/notes.jl index 0f30c12..a273b9d 100644 --- a/src/notes.jl +++ b/src/notes.jl @@ -100,6 +100,7 @@ function find_accidental(which_semitone, noteclass) return Note(noteclass, accidental) end +Note(n::Note) = n const middle_C = C4 diff --git a/src/scales.jl b/src/scales.jl index b5331e0..136c31f 100644 --- a/src/scales.jl +++ b/src/scales.jl @@ -1,79 +1,79 @@ -mutable struct Scale - current::Pitch - steps -end +struct Scale{T<:Union{Note, Pitch}} + tonic::T + steps::Dict{Note, Interval} -# state is a counter that loops through the steps vector -function next_state(s::Scale, state) - if state == length(s.steps) - state = 1 - else - state += 1 - end + function Scale(tonic::T, steps::Vector{Interval}) where {T} + steps_dict = Dict{Note, Interval}() - return state -end + current = tonic + for step in steps + steps_dict[Note(current)] = step + current += step + end -function next_value!(s::Scale, state) - s.current = s.current + s.steps[state] - return s.current + return new{T}(tonic, steps_dict) + end end -Scale(state, steps) = Scale(state, steps, 1) +# Scale2(M.C4, major_scale) -Base.IteratorSize(::Type{Scale}) = Base.IsInfinite() -Base.IteratorEltype(::Type{Scale}) = Base.HasEltype() -Base.eltype(::Type{Scale}) = Pitch +Base.iterate(s::Scale{T}) where {T} = s.tonic -function iterate!(s::Scale, state) - next_value!(s, state) - state = next_state(s, state) +function Base.iterate(s::Scale{T}, p::T) where {T} + n = Note(p) - return state -end + !haskey(s.steps, n) && error("Note $n not in scale") -function Base.iterate(s::Scale, state=1) - current = s.current - state = iterate!(s, state) + step = s.steps[n] + new_pitch = p + step - return current, state + return p, new_pitch end +Base.IteratorSize(::Type{Scale{T}}) where {T} = Base.IsInfinite() +Base.IteratorEltype(::Type{Scale{T}}) where {T} = Base.HasEltype() +Base.eltype(::Type{Scale{T}}) where {T} = T + -major_scale = let M = Major_2nd, m = Minor_2nd +const major_scale = let M = Major_2nd, m = Minor_2nd [M, M, m, M, M, M, m] end -natural_minor_scale = let M = Major_2nd, m = Minor_2nd -[M, m, M, M, M, m, M] +const natural_minor_scale = let M = Major_2nd, m = Minor_2nd + [M, m, M, M, M, m, M] end -melodic_minor_scale = let M = Major_2nd, m = Minor_2nd -[M, m, M, M, M, M, m] +const melodic_minor_scale = let M = Major_2nd, m = Minor_2nd + [M, m, M, M, M, M, m] end -harmonic_minor_scale = let M = Major_2nd, m = Minor_2nd -[M, m, M, M, m, Interval(1, Augmented), m] +const harmonic_minor_scale = let M = Major_2nd, m = Minor_2nd + [M, m, M, M, m, Interval(1, Augmented), m] end -s = Scale(MusicTheory.C4, major_scale) +# s = Scale(MusicTheory.D4, major_scale) + + +# popfirst!(s) + +# collect(Iterators.take(s, 20)) + +# s2 = Base.Iterators.Stateful(Scale(MusicTheory.C4, harmonic_minor_scale)) + +# Base.Iterators.take(s2, 10) |> collect -s +# eltype(Scale) -s = Base.Iterators.Stateful(Scale(MusicTheory.C4, major_scale)) -popfirst!(s) +# # @edit Base.Iterators.Stateful(Scale(MusicTheory.C4, major_scale)) -Iterators.take(s, 10) |> collect -s2 = Base.Iterators.Stateful(Scale(MusicTheory.C4, harmonic_minor_scale)) +# interval(C4, B4) -Base.Iterators.take(s2, 10) |> collect -eltype(Scale) -@edit Base.Iterators.Stateful(Scale(MusicTheory.C4, major_scale)) \ No newline at end of file +# iterate(s) \ No newline at end of file