Abjad / abjad

Abjad is a Python API for building LilyPond files. Use Abjad to make PDFs of music notation.
https://abjad.github.io
GNU General Public License v3.0
239 stars 40 forks source link

How to spell notes in a scale? #1378

Closed Bk8 closed 2 years ago

Bk8 commented 2 years ago

Hi, I'm using Abjad to generate scales from interval arrays. I tried to build a simple Chromatic scale this way but I'm getting way too much accidentals for the note names. I'm retrieving this information from a huge list of scales intervals so I'm trying to solve this issue automatically. Is there any way to fix this?

Thanks for your time.

Code:

def makeScale(tonic, interval_segment):
    pitches = []
    pitch = abjad.NamedPitch(tonic)
    pitches.append(pitch)
    for interval in interval_segment:
        pitch = pitch + interval
        pitches.append(pitch)
    pitch_segment = abjad.PitchSegment(pitches)
    return pitch_segment

print(makeScale("C'", abjad.IntervalSegment(['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'])))
#Result
#<c' df' eff' fff' gfff' affff' bfffff' cfffff'' dssssss' esssss' fsssss' gssss' asss'>
trevorbaca commented 2 years ago

Surprisingly, the result you're getting is correct: a minor second above C is D-flat; a minor second above D-flat is E-double-flat (!); a minor second above E-double-flat is F-double-flat; and so on. The logic of the loop is correct, but better control over the spelling of the scale is missing.

The usual spelling of a C-ascending chromatic scale is C-C#-D-D#-E-F-F#-G-G#-A-A#-B-C, which is actually a little tricky. The intervals between notes aren't all minor seconds: the interval from C to C# is an augmented unison, for example, just like the interval from D to D#. You can specify the sequence of adjacent intervals like this:

intervals = ["A1", "m2", "A1", "m2", "m2", "A1", "m2", "A1", "m2", "A1", "m2"]

Then your code produces the expected output:

def make_scale(tonic, intervals):
    pitches = []
    pitch = abjad.NamedPitch(tonic)
    pitches.append(pitch)
    for interval in intervals:
        pitch = pitch + interval
        pitches.append(pitch)
    pitch_segment = abjad.PitchSegment(pitches)
    return pitch_segment

intervals = ["A1", "m2", "A1", "m2", "m2", "A1", "m2", "A1", "m2", "A1", "m2"]
make_scale(0, intervals)
PitchSegment("c' cs' d' ds' e' f' fs' g' gs' a' as' b'")

On the other hand, if you're thinking about the interval-content of scales numerically ("1 semitone between each note in the scale") then maybe this means that the accidentals used to spell the scale don't matter too much? If so, working with pitches as numbers is straightforward:

def make_scale(tonic, intervals):
    numbers = abjad.math.cumulative_sums(intervals, start=tonic)
    pitches = [abjad.NamedPitch(_) for _ in numbers]
    return abjad.PitchSegment(pitches)

make_scale(0, 12 * [1])
PitchSegment("c' cs' d' ef' e' f' fs' g' af' a' bf' b' c''")
Bk8 commented 2 years ago

Thanks for the response, it was useful. Also, I found that giving names to musical notes in a scale is not a trivial task and I'm researching algorithm to actually do this.

A function to respell a pitch could be useful on these cases for custom names, it could take as a parameter another pitch. For example respell 'cs' as 'd' and get as a result 'df'. Maybe any chance to add something like this?

trevorbaca commented 2 years ago

Use abjad.iterpitches.respell_with_flats() or abjad.iterpitches.respell_with_flats() on notes and chords:

staff = abjad.Staff("c'8 cs' d' ef' e' f' fs' g' af' a' bf' b' c''")
abjad.show(staff)
default
abjad.iterpitches.respell_with_flats(staff)
abjad.show(staff)
flats
abjad.iterpitches.respell_with_sharps(staff)
abjad.show(staff)
sharps

Use _respell() on pitches:

>>>pitch = abjad.NamedPitch("cs'")
>>> pitch._respell(accidental="flats")
NamedPitch("df'")
martin-ribot commented 2 years ago

@Bk8 also have in mind that spelling a scale of 7 notes or less is not the same as spelling one of more than seven notes. With seven notes you can use the whole set of basic note names (German "Stammton", not sure how to translate that), and you would just simply add the corresponding accidental to each note, ex. c df e fs g af b. With more than seven notes it becomes trickier, and I found it better to spell notes in context, i.e. not as a scale. In this situation I prefer perfect, minor and major intervals than augmented and diminished whenever possible. I wrote an algorithm for that that works on "actual music" (not on a list of pitches, I think), in case that's something that you think might work for you. —Martín.