Skip to content

Commit

Permalink
Bugfix, flat notes in name_to_pitch, pitch_to_name (#128)
Browse files Browse the repository at this point in the history
* Bugfix for single character note names

* Flat notes in name_to_pitch, pitch_to_name

* Bump to v1.9.0, update changelog
  • Loading branch information
stellartux authored Mar 25, 2021
1 parent f864243 commit 7be2697
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 17 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
# v1.9.0
* `name_to_pitch` now accepts flat names and `pitch_to_name` accepts `flat` keyword argument.
# v1.8.0
* `DrumNote` shorthand constructor, keyword constructor for `Note` and added `DRUMKEY` dictionary in constants.jl (originally in MusicVisualizations).
# v1.7.0
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "MIDI"
uuid = "f57c4921-e30c-5f49-b073-3f2f2ada663e"
repo = "https://github.com/JuliaMusic/MIDI.jl.git"
version = "1.8.1"
version = "1.9.0"

[compat]
julia = "1"
Expand Down
43 changes: 27 additions & 16 deletions src/note.jl
Original file line number Diff line number Diff line change
Expand Up @@ -119,34 +119,41 @@ Base.copy(notes::Notes) = Notes([copy(n) for n in notes], notes.tpq)
#######################################################
# string name <-> midi pitch
#######################################################
using Base.Meta, Base.Unicode
using Base.Unicode
const PITCH_TO_NAME = Dict(
0=>"C", 1=>"C♯", 2=>"D", 3=>"D♯", 4=>"E", 5=>"F", 6=>"F♯", 7=>"G", 8=>"G♯", 9=>"A",
10 =>"A♯", 11=>"B")
const NAME_TO_PITCH = Dict(
v => k for (v, k) in zip(values(PITCH_TO_NAME), keys(PITCH_TO_NAME)))

(v => k for (v, k) in zip(values(PITCH_TO_NAME), keys(PITCH_TO_NAME)))...,
"D♭"=>1, "E♭"=>3, "G♭"=>6, "A♭"=>8, "B♭"=>10)
const SHARP_TO_FLAT = Dict(
"C♯"=>"D♭", "D♯"=>"E♭", "F♯"=>"G♭", "G♯"=>"A♭", "A♯"=>"B♭"
)
"""
pitch_to_name(pitch) -> string
pitch_to_name(pitch; flat=false) -> String
Return the name of the pitch, e.g. `F5`, `A♯3` etc. in modern notation given the
pitch value in integer.
pitch value in integer. When `flat=true`, accidentals are printed as flats,
e.g. `A♯3` is printed as `B♭3`.
Reminder: middle C has pitch `60` and is displayed as `C4`.
"""
function pitch_to_name(j)
i = Int(j)
function pitch_to_name(pitch; flat::Bool=false)
i = Int(pitch)
notename = PITCH_TO_NAME[mod(i, 12)]
if flat
notename = get(SHARP_TO_FLAT, notename, notename)
end
octave = (i÷12)-1
return notename*string(octave)
end

"""
name_to_pitch(p::String) -> Int
Return the pitch value of the given note name `p`, which can be of the form
name_to_pitch(name::String) -> Int
Return the pitch value of the given note name, which can be of the form
`capital_letter*sharp*octave` where:
* `capital_letter` : from `"A"` to `"G"`.
* `sharp` : one of `"#"` `"♯"` or `""`.
* `sharp` : one of `"#"` `"♯"` `"b"` `"♭"` or `""`.
* `octave` : any integer (as a string), the octave number (an octave is 12 pitches).
If not given it is assumed `"5"`.
Expand All @@ -156,15 +163,19 @@ We define E.g. `name_to_pitch("C4") === 60` (i.e. string
See http://newt.phys.unsw.edu.au/jw/notes.html
and https://en.wikipedia.org/wiki/C_(musical_note) .
"""
function name_to_pitch(p)
pe = collect(Unicode.graphemes(p))
function name_to_pitch(name)
pe = collect(Unicode.graphemes(name))
pitch = NAME_TO_PITCH[pe[1]]
x = 0
if pe[2] == "#" || pe[2] == ""
x = 1
if length(pe) >= 2
if pe[2] == "#" || pe[2] == ""
x = 1
elseif pe[2] == "b" || pe[2] == ""
x = -1
end
end
if length(pe) > 1 + x
octave = Meta.parse(join(pe[2+x:end]))
if isdigit(first(last(pe)))
octave = parse(Int, join(filter(isdigit first, pe)))
else
octave = 4
end
Expand Down
5 changes: 5 additions & 0 deletions test/note.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ end
@test pitch_to_name(name_to_pitch("B#")) == "C5"
@test pitch_to_name(name_to_pitch("F♯")) == "F♯4"
@test name_to_pitch("C4") == 60
@test pitch_to_name(name_to_pitch("C")) == "C4"
@test pitch_to_name(name_to_pitch("Cb")) == "B3"
@test pitch_to_name(name_to_pitch("G♭")) == "F♯4"
@test pitch_to_name(name_to_pitch("G♭"); flat=true) == "G♭4"
@test pitch_to_name(name_to_pitch("Bb7")) == "A♯7"

n = Note(0, 1, 1, 1)
@test pitch_to_name(n.pitch) == "C-1"
Expand Down

0 comments on commit 7be2697

Please sign in to comment.