1/* notes.pl 2Author: Gimenez, Christian. 3 4Copyright (C) 2025 Gimenez, Christian 5 6This program is free software: you can redistribute it and/or modify 7it under the terms of the GNU General Public License as published by 8the Free Software Foundation, either version 3 of the License, or 9at your option) any later version. 10 11This program is distributed in the hope that it will be useful, 12but WITHOUT ANY WARRANTY; without even the implied warranty of 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14GNU General Public License for more details. 15 16You should have received a copy of the GNU General Public License 17along with this program. If not, see <http://www.gnu.org/licenses/>. 18 192025-10-12 20*/ 21 22:- module(notes, [ 23 note_order/1, 24 notenum/2, 25 sum_semitones/3, 26 sum_tones/3, 27 tone/2, 28 semitone/2, 29 semitones/3, 30 interval/2, 31 interval/3, 32 second/3, 33 third/3, 34 fourth/2, 35 fifth/2, 36 sixth/3, 37 seventh/3, 38 eighth/2, 39 octave/2, 40 transpose/3, 41 transpose/4 42 ]).
93:- license(gplv3).
98note_order([c, cs, d, ds, e, f, fs, g, gs, a, as, b]).notenum(c, R) will result in R = 0, and without more results.
But notenum(c, 12) is also true (indicating that C note is also represented
as 12).
Representing notes in numbers are easy for calculations. Adding an amount of semitones to a given note represented as a number instead as a Prolog atom is more efficient.
If Num is a negative number or greater than 11, it is calculated to be between 0 (c note) to 11 (b note).
If Note is not a valid note, Num is -1.
117notenum(c, 0) :- !. 118notenum(cs, 1) :- !. 119notenum(d, 2) :- !. 120notenum(ds, 3) :- !. 121notenum(e, 4) :- !. 122notenum(f, 5) :- !. 123notenum(fs, 6) :- !. 124notenum(g, 7) :- !. 125notenum(gs, 8) :- !. 126notenum(a, 9) :- !. 127notenum(as, 10) :- !. 128notenum(b, 11) :- !. 129notenum(Note, Num) :- 130 % Case: Num is not between 0 and 11 semitones. 131 var(Note), 132 (Num > 11 ; Num < 0), !, 133 134 ResultNum is Num mod 12, 135 notenum(Note, ResultNum). 136notenum(_, -1) :- !.
If StartNote is not a valid note, ignore it.
147sum_semitones(Note-Octave-Duration, Semitones, ResultNote-ResultOctave-Duration) :- !, 148 % Case: StartNote is a full note description 149 sum_semitones(Note-Octave, Semitones, ResultNote-ResultOctave). 150sum_semitones(Note-Octave, Semitones, ResultNote-ResultOctave) :- 151 % Case: StartNote is a partial note description and Note is valid. 152 notenum(Note, Num), Num >= 0, !, 153 ResultNum is Num + Semitones, 154 notenum(ResultNote, ResultNum), 155 156 Diff is (ResultNum div 12), 157 158 ResultOctave is Octave + Diff. 159sum_semitones(Note, Semitones, ResultNote) :- 160 % Case: StartNote is only a note and is valid. 161 atom(Note), 162 notenum(Note, Num), Num >= 0, !, 163 ResultNum is Num + Semitones, 164 notenum(ResultNote, ResultNum). 165sum_semitones(Note, _, Note). % Case: StartNote is not a valid note, then just ignore.
173sum_tones(Note, Tones, ResultNote) :-
174 Semitones is Tones * 2,
175 sum_semitones(Note, Semitones, ResultNote).
183tone(Note, ResultNote) :-
184 sum_semitones(Note, 2, ResultNote).
192semitone(Note, ResultNote) :-
193 sum_semitones(Note, 1, ResultNote).203semitones(FromNote-FromOctave, ToNote-ToOctave, Diff) :- !, 204 semitones(FromNote, ToNote, Diff1), 205 Diff is Diff1 + (ToOctave - FromOctave) * 12. 206semitones(FromNote, ToNote, Diff) :- 207 notenum(FromNote, Num), 208 notenum(ToNote, Num2), 209 Diff is Num2 - Num.
In the literature, the interval between 4-perfect and 5-perfect (6 semitones) has no name. In this implementation, the term 4-extra is assigned to this interval.
222interval(1-perfect, 0) :- !. 223interval(2-minor, 1) :- !. 224interval(2-major, 2) :- !. 225interval(3-minor, 3) :- !. 226interval(3-major, 4) :- !. 227interval(4-perfect, 5) :- !. 228interval(4-extra, 6) :- !. % 6 semitones interval has no name actually. 229interval(5-perfect, 7) :- !. 230interval(6-minor, 8) :- !. 231interval(6-major, 9) :- !. 232interval(7-minor, 10) :- !. 233interval(7-major, 11) :- !. 234interval(Num-Type, Semitones) :- 235 (Num > 7 ; Num < 1), !, 236 237 Octave is Num div 8, 238 Num2 is Num mod 7, 239 interval(Num2-Type, Semitones1), 240 241 Semitones is (Octave * 11) + Semitones1 + 1.
246interval(Interval, StartNote, EndNote) :-
247 interval(Interval, Semitones),
248 sum_semitones(StartNote, Semitones, EndNote).
255second(Type, A, B) :-
256 interval(2-Type, Semitones),
257 sum_semitones(A, Semitones, B).
264third(Type, A, B) :-
265 interval(3-Type, Semitones),
266 sum_semitones(A, Semitones, B).
271fourth(A, B) :-
272 sum_semitones(A, 5, B).
277fifth(A, B) :-
278 sum_semitones(A, 7, B).
285sixth(Type, A, B) :-
286 interval(6-Type, Semitones),
287 sum_semitones(A, Semitones, B).
294seventh(Type, A, B) :-
295 interval(7-Type, Semitones),
296 sum_semitones(A, Semitones, B).Same as octave/2.
303eighth(A, B) :-
304 sum_semitones(A, 12, B).Same as eighth/2.
311octave(A, B) :- 312 sum_semitones(A, 12, B). 313 314sum_semitones_int(Semitones, Note, Note2) :- 315 sum_semitones(Note, Semitones, Note2).
320transpose(Notes, Semitones, NewNotes) :-
321 maplist(sum_semitones_int(Semitones), Notes, NewNotes).
332transpose(Notes, FromNote, ToNote, NewNotes) :-
333 semitones(FromNote, ToNote, Diff),
334
335 transpose(Notes, Diff, NewNotes)
notes: Simple notes representation and calculation.
Predicates that calculates semitones, tones, and intervals in notes.
Names
In order to create a more convenient way to programming with Prolog, name of notes, intervals, and othe possible concepts are different from literature. It may be different from Lilypond or other software too.
For instance, C3 is not used to represent C note in Prolog in third octave because the it would be interpreted as a Prolog variable. Also,
'C3'orc3wolud requier extra processing to separate the octave number from the C/c. However, thec-3term would represent the C note at 3 octave effectively, and the octave number is already separated from the note name. A simmilar approach is implemented for intervals and other concepts.Note representation
Notes are terms with the following possible representations:
c(C note),ds(D# or D sharp), etc.Note-Octave. For example:c-2(C2 note),ds-2(D#2 or D2 sharp), etc.Only sharp notation is used to represent the notes in Prolog (as in
csfor C sharp). No bemol are used in this implementation (ebis not considered as E bemol, insteaddsshould be used to represent the same note).Intervals
Interval calculations can be achieved by the interval/2 and interval/3 predicates, or by using sum_semitones/3 with the specific amount of semitones. There are also aliases for second/3, third/3, etc.
Intervals are represented as:
Nth-Type. Where Nth is the number of interval, and the Type is one of the atoms:perfect,major,minor, orextra. The4-extraatom is used to assign a name to the interval between the P4 (perfect 4th) and P5 (perfect 5th) which has 6 semitones of distance. This interval name does not exists in the literature, and is used to complete the names to a semitone distance in case the calculations results in that number.