8
\$\begingroup\$

Challenge

To quote Wikipedia:

An octave-repeating scale can be represented as a circular arrangement of pitch classes, ordered by increasing (or decreasing) pitch class. For instance, the increasing C major scale is C–D–E–F–G–A–B–[C], with the bracket indicating that the last note is an octave higher than the first note.

Major scales are defined by their combination of semitones and tones (whole steps and half steps):

Tone – Tone – Semitone – Tone – Tone – Tone – Semitone

Or in whole steps and half steps, it would be:

Whole – Whole – Half – Whole – Whole – Whole – Half

So, for example, in the C major scale, we first start with the C note. enter image description here Then we go up a tone (whole step) to a D enter image description here Another tone (whole step) to an E enter image description here Now a semitone (half step) to an F enter image description here Tone (whole step) to a G enter image description here Tone (whole step) to an A enter image description here Tone (whole step) to a B enter image description here And finally, a semitone (half step) to a C again enter image description here A minor scale (I'm talking about the natural minor scale as opposed to the harmonic minor scale and the melodic minor scale) follows the following formula

Tone – Semitone – Tone – Tone – Semitone – Tone – Tone

or

Whole – Half  – Whole – Whole – Half – Whole – Whole 

So, the C minor scale will look like enter image description here or, as letters: C, D, D#, F, G, G#, A#

So, your job today is: given a major or minor scale, print the notes.

Input/Output

  • Input/Output can be taken in any reasonable format for taking the name of the scale and returning the set of the increasing octave-repeating notes of that scale.

  • You don't need to print out the last note.

  • If the notes are enharmonic equivalent (same note but different names, eg A#/Bb), you can print either of them, but you can't print C as B# or E as Fb)

  • If the scales are enharmonic equivalent (same scale but different names eg G#m and Abm), you have to handle both of them.

Input -> Output
C -> [C, D, E, F, G, A, B]
Cm -> [C, D, Eb (D#), F, G, Ab (G#), Bb (A#)]
G -> [G, A, B, C, D, E, F# (Gb)]
F#m -> [F# (Gb), G# (Ab), A, B, C# (Db), D, E]

This is , so the shortest answer (in bytes) wins!

\$\endgroup\$
6
  • 1
    \$\begingroup\$ Normally, a diatonic scale (in any mode) is expected to have exactly one occurrence of each of the letters A-G, so your minor example would actually use the enharmonic flats instead of the sharps - Cm=C/D/Eb/F/G/Ab/Bb. A music theoretician wouldn't consider the example as given to be correct. \$\endgroup\$ Commented Jan 20, 2023 at 15:00
  • 1
    \$\begingroup\$ @JeffZeitlin I know that, but this challange is meant just to print out the correct note, what note to use is not too important. \$\endgroup\$
    – badatgolf
    Commented Jan 20, 2023 at 15:28
  • 2
    \$\begingroup\$ It's also correct to use B♯ in the scale of C♯ major and F♭ in the scale of A♭ minor. \$\endgroup\$
    – Neil
    Commented Jan 20, 2023 at 20:09
  • 1
    \$\begingroup\$ 1. You seem to want the Natural minor (for example Am uses the same notes as C major) and not any of the variations (harmonic/melodic minor.) 2. A#m (same notes as C#maj)is rare, as it uses 7 sharps, as the alternative Bbm (same notes as Dbmaj) uses only 5 flats. So I understand you require all 15 key signatures (from 7 flats through 7 sharps) in both major and minor. That's major keys: Cb Gb Db Ab Eb Bb F [C] G D A E B F# C# and minor keys Abm Ebm Bbm Fm Cm Gm Dm [Am] Em Bm F#m C#m G#m D#m A#m [zero flats /sharps in square brackets] , correct? Going beyond that requires double flats/sharps. \$\endgroup\$ Commented Jan 21, 2023 at 1:12
  • 2
    \$\begingroup\$ The musical notation looks fancy, but are you sure that the 'to a B' and 'to a C' lines actually show 'B' and 'C' notes? \$\endgroup\$ Commented Jan 21, 2023 at 20:44

7 Answers 7

10
\$\begingroup\$

Vyxal, 27 bytes

-1 byte thanks to AndrovT

First input is the note, second is the scale; 0 for minor and 1 for major.

kA7Ẏ:\#ẊY3⟇8⟇~ḟǓ»°f»b⁰9*ǔ*ꜝ

I'm a bit rusty.

Try it Online! | 35 bytes if you have to handle flats

Explanation:

kA7Ẏ:\#ẊY3⟇8⟇~ḟǓ»°f»b⁰9*ǔ*ꜝ
kA7Ẏ                           Uppercase alphabets A-H
    :\#ẊY                      Duplicate, take cartesian product with "#", interleave
         3⟇8⟇                  Remove third and remove eight char (I know, very cringe)
             ~ḟǓ               Find index of note, rotate list by index
                »°f»b          Compressed number 2906, binary "101101011010"
                     ⁰9*       Multiply scale by 9, pushes 9 for major and 0 for minor
                        ǔ      Rotate "101101011010" by 0 or 9
                         *ꜝ    Filter out list by binary string
\$\endgroup\$
4
  • \$\begingroup\$ 27 bytes \$\endgroup\$
    – AndrovT
    Commented Jan 20, 2023 at 19:56
  • \$\begingroup\$ @AndrovT noice, I knew there was a better way to filter by truthy indices \$\endgroup\$
    – math scat
    Commented Jan 20, 2023 at 20:01
  • 2
    \$\begingroup\$ "I'm a bit rusty" - No you're not, you're mathcat! \$\endgroup\$
    – The Thonnu
    Commented Jan 20, 2023 at 20:18
  • \$\begingroup\$ "If the scales are enharmonic equivalent (same scale but different names eg G#m and Abm), you have to handle both of them" - this doesn't seem to handle names using flats (Ab, Bb, Db, and so on...)... \$\endgroup\$ Commented Jan 21, 2023 at 20:06
6
\$\begingroup\$

JavaScript (Node.js), 137 bytes

(B,[E]=B.split`m`,h=E=>E[1]>{}?E[0]:E[1]||{B,E}[E]?'ABCDEFG'['0x'+E[0]-9|0]:E+'#',g=k=>[E,...k?g(k>>1,E=h(k&1?h(E):E)):[]])=>g(E<B?45:59)

Try it online!

JavaScript (Node.js), 140 bytes

f=(x,i,g=k=>[R[i%12],...k?g(k>>1,i+=7-k%2*5):[]])=>x==R[i]?g(59):x==R[i]+'m'?g(45):f(x,-~i)
R='BGbDbAbEbBbFCGDAEBF#C#G#D#A#'.match(/.#?b?/g)

Try it online!


JavaScript (Node.js), 119 bytes

m=>E=>(B=a=>[E,...a?B(a>>1,E=(h=E=>E[1]>B?E[0]:E[+!{B,E}[E]]?'ABCDEFG'['0x'+E[0]-9|0]:E+'#')(a&1?h(E):E)):[]])(m?45:59)

Try it online!

taking minor as argument

As reference since others do this

m=>E=>(
h=E=>E[1]>B?     // B == 'a=>...', >B is 'b'
  E[0]           // remove 'b'
:
  E[+!{B,E}[E]]? // if E=='B' or E=='E'
                 // then +!truthy=0 aka len>=1
                 // else +!falsy=1 aka len>=2
    'ABCDEFG'['0x'+E[0]-9|0]
                 // 0xA-9==1 0xF-9==6 0xG-9==NaN
  :E+'#',        // Else sharpen
B=a=>[
  E,             // This
  ...a?B(        // Loop through
    a>>1,
    E=h(a&1?h(E):E)
  ):[]]          // twice for set bit
)(m?45:59)
\$\endgroup\$
5
\$\begingroup\$

05AB1E, 29 bytes

Au'#æâJŽyhbÏDIkƵ;ºŽ∍ÁĆ‚Iè.¥+è

Two loose inputs, where the first input is the note and second is the scale as 1 for major and 0 for minor. Output as a list of notes. I/O uses high-pitched notes (with #).

Try it online or verify all test cases.

Explanation:

Au'#æâJŽyhb '# Push notes-list ["A","A#","B","C","C#","D","D#","E","F","F#","G","G#"]:
Au           #  Push the uppercase alphabet
  '#        '#  Push character "#"
    æ        #  Pop and push its powerset to pair it with an empty string: ["","#"]
     â       #  Take the cartesian product with the alphabet
      J      #  Join each inner pair together to a string
       Žyh   #  Push compressed integer 15343
          b  #  Convert it to binary: 11101111101111
           Ï #  Keep the strings at the truthy (==1) indices
D            # Duplicate the list of notes
 Ik          # Pop the copy, and get the index of the first input-note
Ƶ;           # Push compressed integer 212
  º          # Mirror it to 212212
Ž∍Á          # Push compressed integer 22122
   Ć         # Enclose; append its own head: 221222
    ‚        # Pair them together: [212212,221222]
     Iè      # Keep the one at the second input-scale's index
       .¥    # Undelta with leading 0 (212212→[0,2,3,5,7,8,10]; 221222→[0,2,4,5,7,9,11])
         +   # Add the index of the input-note to each
          è  # Use that list to index into the notes-list
             # (after which the resulting list of notes is output implicitly)

See this 05AB1E tip of mine (section How to compress large integers?) to understand why Žyh is 15343; Ƶ; is 212; and Ž∍Á is 22122.

\$\endgroup\$
5
  • \$\begingroup\$ "If the scales are enharmonic equivalent (same scale but different names eg G#m and Abm), you have to handle both of them." - This appears to give identical results for G# and Gb... \$\endgroup\$ Commented Jan 21, 2023 at 20:08
  • \$\begingroup\$ @DominicvanEssen That's a coincidence, since I don't support b notes as I/O.. The G# results in an index of 11 with a list of indices [11,13,15,16,18,20,22] and the Gb results in an index of -1 with a list of indices -1,1,3,4,6,8,10], which with modular 0-based indexing happens to be the same output-list ["G#","A#","C","C#","D#","F","G"]. If you put in bob as input, it'll still result in that same -1 index and output-list. 🤷 \$\endgroup\$ Commented Jan 21, 2023 at 20:12
  • \$\begingroup\$ Ah, that's a funny coincidence! But, apart from that, doesn't the challenge require programs to handle both X# and Xb names for each scale...? \$\endgroup\$ Commented Jan 21, 2023 at 20:16
  • \$\begingroup\$ @DominicvanEssen It is?.. I thought we could choose either high- (#) or low-pitched (b) I/O based on rule "If the notes are enharmonic equivalent, you can print either of them". Tbh, I've never played any instruments and had to look up the notes in the first place (and remembered doing this challenge before). The challenge is all but self-explanatory and the rules and I/O are pretty confusing imo.. :/ \$\endgroup\$ Commented Jan 21, 2023 at 20:24
  • \$\begingroup\$ Agree that challenge is not at all self-explanatory... \$\endgroup\$ Commented Jan 21, 2023 at 20:26
5
\$\begingroup\$

Charcoal, 63 bytes

≔FCGDAEBηF⁺⁻⁵I⪪§⪪”)¶▶↷“Mν&”⁷№θm¹⁺⌕η§θ⁰×⁷⁻№θ#№θb«§ηι×#÷ι⁷×b±÷ι⁷→

Try it online! Link is to verbose version of code. Explanation: Based on my answer to Output the 12 Bar Blues so has all of the caveats of that answer i.e. it uses strict music theoretical scales and works with any number of sharps or flats except that it uses ## instead of x.

≔FCGDAEBη

Get the list of fifths.

F⁺⁻⁵I⪪§⪪”)¶▶↷“Mν&”⁷№θm¹⁺⌕η§θ⁰×⁷⁻№θ#№θb«

Split the compressed string 53164205386497 into digits, taking the first or second half depending on whether a major or minor scale is required, subtract each digit from 9, and add on the index of the input in the list of fifths, adding 7 for each #, and subtracting 7 for each b. Loop over the resulting list.

§ηι

Output the appropriate note by cyclic indexing.

×#÷ι⁷×b±÷ι⁷

Output the appropriate number of sharps or flats.

Leave a space between notes.

\$\endgroup\$
5
\$\begingroup\$

JavaScript (Node.js), 108 bytes

-3 thanks to @l4m2

Expects (m, s), where m is a binary flag (0 for major, 1 for minor). Returns a comma-separated string of notes.

f=(m,s,n=59-14*m)=>n?s+[,f(B=Buffer,B((h=([a,b])=>a%3*!b?[a,35]:[b%5?a:~-a%7+65])((n&1?h:B)(B(s)))),n>>1)]:s

Try it online!

How?

The algorithm is designed to go directly from one note to the next by adding a semitone. Depending on the scale structure, this is done either once or twice per iteration.

Commented

f = (                 // f is a recursive function taking:
  m, s,               //   m = 'minor' flag, s = note string
  n = 59 - 14 * m     //   n = scale bitmask (major: 111011, minor: 101101)
) =>                  //
n ?                   // if n is not equal to 0:
  s + [,              //   append s, followed by a comma, followed by ...
    f(                //   ... the result of a recursive call:
      B = Buffer,     //     B = alias for Buffer (m is not needed anymore)
      B(              //     turn back into a string ...
        ( h =         //       ... the result of the helper function h
          ([a, b]) => //       taking (a, b) = (note, symbol) as ASCII codes
          a % 3 *     //         if the note is neither 'B' nor 'E'
          !b ?        //         and there's no symbol:
            [ a, 35 ] //           leave the note unchanged and append a '#'
          :           //         else:
            [ b % 5 ? //           if the symbol is 'b':
                a     //             just remove the 'b'
              :       //           else:
                ~-a   //             advance to the next note
                % 7   //             (make sure to wrap from 'G' to 'A')
                + 65  //             and don't put any symbol
            ]         //
        )(            //       invoke h:
          ( n & 1 ? h //         twice if the LSB of n is set --> h(h(B(s))
                  : B //         or just once otherwise --> h(B(B(s))
          )(B(s))     //         note that B(B(s)) is equivalent to B(s)
        )             //
      ),              //     end of Buffer()
      n >> 1          //     right-shift n by 1 position
    )                 //   end of recursive call
  ]                   //
:                     // else:
  s                   //   append the last note and stop the recursion

JavaScript (Node.js), 131 bytes

Supporting the format used in the test cases (e.g. F#m) is rather costly.

f=(n,s=n.replace(/m/,_=>(n=45,''),n=59))=>n?s+[,f(n>>1,(B=Buffer)((h=([a,b])=>a%3*!b?[a,35]:[b%5?a:~-a%7+65])((n&1?h:B)(B(s)))))]:s

Try it online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 117 \$\endgroup\$
    – l4m2
    Commented Jan 20, 2023 at 19:13
4
\$\begingroup\$

C (gcc), 211 209 203 bytes

  • -6 thanks to ceilingcat

Takes a key in note[accidental][minor] form: e.g. C, Bb, G#m

char*a="A#BC#D#EF#G#",*d="b-#",*u;t,l,v;f(char*s){t=index(a,*s++)-a;(u=index(d,*s))&&*u&&(s++,t=(t+u-d+11)%12);l=*s?180:236;for(t--;l/=2;printf("%c%c ",a[v-35?t:(t+11)%12],v-35?32:v))v=a[t=(l%2-~t)%12];}

Try it online!

char*a="A#BC#D#EF#G#", // Notes (ignoring sharps/flats)
    *d="b-#", // Sharp/nothing/flat (used to adjust a note)
    *u; // Used for determining the existence of a sharp/flat
t,l,v; // Index into notes, scale pattern, current note when doing scale
f(char*s){
  t=index(a,*s++)-a; // Find base note
  (u=index(d,*s))&&*u&&(s++,t=(t+u-d+11)%12); // Find sharp/flat: adjust index accordingly
  l=*s?180:236; // Find major/minor pattern (each has a starting +1 to help the upcoming loop)

  for(t--; // Decrement the starting index to allow the code to do the printing stuff once in the loop
      l/=2; // Keep printing the scale until done
      printf("%c%c ",
        a[v-35?t:(t+11)%12], // Adjust to the base note if needed
        v-35?32:v)) // Print a sharp if needed
    v=a[t=(l%2-~t)%12]; // Get the next note
}
\$\endgroup\$
1
1
\$\begingroup\$

Thunno, \$ 70 \log_{256}(96) \approx \$ 57.62 bytes

AZ7RAI'#d""TrsZ!.J15343bZZgAJ'1=k.AKDz0sAh212212 221222ZPz1AIdz(0ZN+AI

Attempt This Online!

A very long port of Kevin Cruijssen's 05AB1E answer.

Explanation

AZ7RAI     # The first 7 letters of the alphabet, A to G
      '#d '# The list ['#']
""T        # With "" appended to it
   r       # Reversed
    sZ!    # Then take the cartesian product of that and A-G
       .J  # Join each inner list
15343bZZ   # Zipped with the binary representation of 15343
        g  # Filtered of where:
AJ         #   the first element
  '1=     '#   is a 1
     k     # (End filter)
      .AK  # Last element of each
         D # Duplicate this
z0sAh      # Get the index of the first input in this list
212212     # The number 212212
221222     # And the number 221222
      ZP   # Paired together
z1AI       # Index the second input into this list
    dz(    # Cumulative sums of this number
       0ZN # With a 0 prepended
+          # Added to the index of the first input in the list
 AI        # Indexed into that list
           # Implicit output
\$\endgroup\$
1
  • \$\begingroup\$ "If the scales are enharmonic equivalent (same scale but different names eg G#m and Abm), you have to handle both of them." - This appears to give identical results for G# and Gb... \$\endgroup\$ Commented Jan 21, 2023 at 20:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.