[daily part] 002_ staff mode derivation

Derivation of staff mode

                        8195.
   it was not until the last two months that the big man suggested that in fact, all kinds of mode lifting marks can be obtained by translating the piano key position, which provides a method to infer the mode: on the basis of the most conventional C major, each tone is increased by one interval, and then you can know how many ascending or descending signs are there by looking at which tones run on the black key.
   although this process is easy to complete by hand, considering that I haven't touched C + + for more than two years after the public elective course of C + +. In order to be familiar with this important language again, this derivation process will be completed in C + +.

Basic ideas

Starting in C Major

   in C major, there is no lifting sign in the staff:

   if you move the key position to the right for two halftones, you can get two rising tones (D major):

   because there are 12 semitones in an octave, a total of 12 modes can be obtained by such translation. As long as the key position of C major is translated 12 times, all twelve modes can be obtained.

Expression of mode in program

   an octave has 12 semitones, and these keys need to be continuously translated in one direction in the derivation process, so the cyclic shift is considered. Use 12 bit s to correspond to 12 semitones C, C#|Db, D, D#|Eb, E, F, F#|Gb, G, G#|Ab, A, A#|Bb and B in an octave respectively. If the key is on that semitone, set 1, otherwise set 0. Every time you shift to the high pitch, the part beyond B will return to the position of C.

    let bit 0 represent C, bit 1 represent C #... And so on, bit 11 represents B. if it is shifted to the left, bit 11 is moved to bit 0:

   for example, if the seven key positions corresponding to C major are C, D, E, F, G, A and B respectively, the corresponding bit sequence is 101010110101. When two semitones are shifted to the high pitch to D major, the corresponding cyclic shift to the left is twice, resulting in 1010110, corresponding to C #, D, E, f #, G, A and B.

  with a clear idea, you can start writing code.

code implementation

Initial state: C Major

   as mentioned earlier, the bit representation of C major, and the initial state is confirmed as C major, so this value can be stored as a constant in the header file:

int MODE = 0xAB5; //101010110101 <- reverse(101011010101)

Correspondence between bit and note

  similar to the dictionary of python, the standard library of C + + also provides the dictionary structure. The structure is stored in the map library, which can be used by std::map (using std::map has been used here, so the map is written directly when using):

map<int, string> NOTE_MAP = {{0, "C",},{1, "C#",},{2, "D",},{3, "D#",},
                             {4, "E",},{5, "F",},{6, "F#",},{7, "G",},
                             {8, "G#",},{9, "A",},{10, "A#",},{11, "B",},
                             {12, "C",},{13, "Db",},{14, "D",},{15, "Eb",},
                             {16, "E",},{17, "F",},{18, "Gb",},{19, "G",},
                             {20, "Ab",},{21, "A",},{22, "Bb",},{23, "B",}};

   24 entries are given here, mainly because some modes use a descending sign. For such modes, the descending sign representation should be used (corresponding to articles 13-24 of the dictionary).

Cyclic shift

   the idea of cyclic shift is very simple, that is, shift to the left once, and then move the highest bit to the first bit. Bits at bit 12 and above are not truncated here, but there is no impact, because subsequent operations will not involve higher bits:

/*
 * brief Opposite mode moves one unit to the left (treble direction)
 *
 * @param orgMode Original mode
 */
void shiftMode(int &orgMode){
  orgMode <<= 1;
  orgMode = orgMode | !!((1 << 12) & orgMode);
}

Print modal information

   there are two modes: ascending and descending. In order to distinguish them, the flag mark of descending tone: dFlag is used here to distinguish them. If there is a falling sign, the entries of items 13-24 will be queried in the dictionary:

/*
 * brief Print information about mode
 *
 * @param mode mode
 * @param dFlag The descending sign indicates that it is 0 by default, that is, the ascending sign
 */
void printModeInfo(int &mode, bool dFlag = false){
  for (int i = 0; i < 12; i++){
      int mask = (1 << i);
      if (mode & mask){
        std::cout << NOTE_MAP[i+12*dFlag] << "\t";
      }
  }
  std::cout << std::endl;
}

main function

  print the result of each shift. Due to previous experience, ascending and descending tones appear alternately, so it is easy to judge whether the value of dFlag is true or false:

int main()
{
  int mapSize = NOTE_MAP.size();
  int shift = 0;
  do{
    printModeInfo(MODE, (((shift <= 5) && (shift % 2)) || ((shift >= 8) && !(shift % 2))) ? true : false);
    shiftMode(MODE);
    shift += 1;
  }
  while (shift < 12);
  return 0;
}

Operation results and conclusions

   after operation, the following outputs are obtained:

C	D	E	F	G	A	B	
C	Db	Eb	F	Gb	Ab	Bb	
C#	D	E	F#	G	A	B	
C	D	Eb	F	G	Ab	Bb	
C#	D#	E	F#	G#	A	B	
C	D	E	F	G	A	Bb	
C#	D#	F	F#	G#	A#	B	
C	D	E	F#	G	A	B	
C	Db	Eb	F	G	Ab	Bb	
C#	D	E	F#	G#	A	B	
C	D	Eb	F	G	A	Bb	
C#	D#	E	F#	G#	A#	B

   this is the derived 12 modes. Note that the number of rising signs and falling signs is symmetrical about C major, which is more intuitive when expressed in images:

  looking clockwise along this image, the mode evolves from C major, D flat major, D major... To B major, and finally back to C major. Each of these major keys has a corresponding minor. Since the law has not been found, it will not be described here.

    for the mode of moving up six semitones in C Major - ascending F major, it is difficult to understand in the output results for the time being.

   through inquiry in overture and online, it is known that the F major is composed of six rising marks or six falling marks:

   and, E#=F, because there is only one semitone between E and F.

   as a result, the question of mode has been clearly answered.

Complete code

main.h

#include <map>
#include <string>

using std::map;
using std::string;

map<int, string> NOTE_MAP = {{0, "C",},{1, "C#",},{2, "D",},{3, "D#",},
                             {4, "E",},{5, "F",},{6, "F#",},{7, "G",},
                             {8, "G#",},{9, "A",},{10, "A#",},{11, "B",},
                             {12, "C",},{13, "Db",},{14, "D",},{15, "Eb",},
                             {16, "E",},{17, "F",},{18, "Gb",},{19, "G",},
                             {20, "Ab",},{21, "A",},{22, "Bb",},{23, "B",}};

int MODE = 0xAB5; //101010110101 <- reverse(101011010101)

main.cpp

#include <iostream>
#include <string>
#include "main.h"

using std::map;
using std::string;

void shiftMode(int &orgMode);
void printModeInfo(int &mode, bool dFlag);

int main()
{
  int mapSize = NOTE_MAP.size();
  int shift = 0;
  do{
    printModeInfo(MODE, (((shift <= 5) && (shift % 2)) || ((shift >= 8) && !(shift % 2))) ? true : false);
    shiftMode(MODE);
    shift += 1;
  }
  while (shift < 12);
  return 0;
}

/*
 * brief Opposite mode moves one unit to the left (treble direction)
 *
 * @param orgMode Original mode
 */
void shiftMode(int &orgMode){
  orgMode <<= 1;
  orgMode = orgMode | !!((1 << 12) & orgMode);
}

/*
 * brief Print information about mode
 *
 * @param mode mode
 * @param dFlag The descending sign indicates that it is 0 by default, that is, the ascending sign
 */
void printModeInfo(int &mode, bool dFlag = false){
  for (int i = 0; i < 12; i++){
      int mask = (1 << i);
      if (mode & mask){
        std::cout << NOTE_MAP[i+12*dFlag] << "\t";
      }
  }
  std::cout << std::endl;
}

Added by toxic_brain on Tue, 28 Dec 2021 11:15:25 +0200