Skip to content

Commit

Permalink
Add scale indexing (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
dpsanders authored Jan 14, 2024
1 parent 75f9350 commit 7970d87
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 2 deletions.
19 changes: 18 additions & 1 deletion src/scales.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ A scale is represented as an iterator.
"""
struct Scale{T<:Union{PitchClass, Pitch}}
tonic::T
notes::Vector{T}
steps::Dict{PitchClass, Interval}

function Scale(tonic::T, steps::Vector{Interval}) where {T}
Expand All @@ -16,12 +17,15 @@ struct Scale{T<:Union{PitchClass, Pitch}}
steps_dict = Dict{PitchClass, Interval}()

current = tonic
notes = T[]

for step in steps
push!(notes, current)
steps_dict[PitchClass(current)] = step
current += step
end

return new{T}(tonic, steps_dict)
return new{T}(tonic, notes, steps_dict)
end
end

Expand All @@ -42,6 +46,19 @@ Base.IteratorEltype(::Type{Scale{T}}) where {T} = Base.HasEltype()
Base.eltype(::Type{Scale{T}}) where {T} = T


function Base.getindex(s::Scale{Pitch}, n::Int)

octave = n ÷ length(s.notes)
n = n % length(s.notes)
if n < 0
n += length(s.notes)
octave -= 1
end

return Pitch(PitchClass(s.notes[n + 1]), s.notes[n + 1].octave + octave)
end


const major_scale = let M = Major_2nd, m = Minor_2nd
[M, M, m, M, M, M, m]
end
Expand Down
12 changes: 11 additions & 1 deletion test/scales.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,14 @@
scale_tones = Base.Iterators.take(scale, 8) |> collect
@test scale_tones == Pitch[C[4], D[4], E[4], F[4], G[4], A[4], B[4], C[5]]

end
@testset "Scale indexing" begin
scale = Scale(D[4], major_scale)
@test scale[0] == D[4]
@test scale[6] == C♯[5]
@test scale[7] == D[5]
@test scale[-1] == C♯[4]
@test scale[-2] == B[3]
end


end

0 comments on commit 7970d87

Please sign in to comment.