0

I have to make a converter that accepts an int representing the chromatic interval of a current tone (i.e. root = 1, minor second = 2, minor seventh = 11, major ninth = 15, major thirteen = 22 etc.), and convert it to the diatonic scale interval and vice versa.

So considering the above example, here are some mappings:

Diat.     |  Chrom.
---------------------
1   (C)   |  1
b2  (Db)  |  2
2   (D)   |  3
4   (F)   |  6
b7  (Bb)  |  11
9   (D)   |  15
#11 (F#)  |  19
b13 (Ab)  |  21

Obviously I can do two functions with a simple 1 to 1 switch statement, or, as suggested, use a bi-directional dictionary, but before doing so I was wondering if I can find more an efficient way to do that.

The diatonic output doesn't have to be a precise tone, enharmonic tones are good too, so D# and Eb are the same.

The parse method however, has to know how to parse both sharps and flats, but this can simply be achieved by setting all sharps to a higher tone flat.

For the chrom. to diat. function, I was thinking more of an algorithm that calculates that. I'm trying to figure it out now, still didn't manage to tho. It has to divide the chromatic steps according to the visual ordering of the piano keyboard, meaning divide the amount in half excluding the half steps between E-F and B-C that are considered whole diatonic steps.

I prefer it to be a calculation rather than a static mapping, so I can later work with transposed scales etc.

Community
  • 1
  • 1
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
  • Why are you concerned about the efficiency of Dictionary here? Will you be using it a million times in a loop? Is it for real-time sound? – SimpleVar May 05 '13 at 04:05
  • @YoryeNathan it should perform very fast. I don't know if million times in a loop, but has to work very fast. – Shimmy Weitzhandler May 05 '13 at 04:10
  • Dictionaries are pretty damn well performing, you know. Have you tried it and seen that it isn't good enough? A faster way would be direct indexing via a static readonly array (very big) array - only possible thanks to the fact that the theory of music is pretty solid. – SimpleVar May 05 '13 at 04:13
  • "I prefer it to be a calculation rather than a static mapping, so I can later work with transposed scales etc." Why is this a problem? After looking up a value in a static mapping, transposition is just a matter of adding a constant. – Karl Knechtel Mar 10 '23 at 05:46

2 Answers2

1

You'd need a special bidirectional Dictionary for this. Here's one: Getting key of value of a generic Dictionary?

Transpositions could be easily handled by modulo arithmetic and by calculating the relative distances between notes.

You could also define class holding pairs of desired elements and put them in a circular linked list or on a binary search tree if You're looking for speed. But I don't think it does really matter when You are dealing with only 12 simple elements.

Community
  • 1
  • 1
Grzegorz Piwowarek
  • 13,172
  • 8
  • 62
  • 93
  • I actually thought about it, but I'm more into an algorithmic way of calculating that. I'm trying to figure it out now, still didn't manage to tho. It has to double the chromatic steps according to the visual ordering of the piano keyboard, meaning except for the half steps between E-F and B-C, it should divide the step amount in half. – Shimmy Weitzhandler May 05 '13 at 03:46
  • I don't really understand why "algorithmic" approach would be better than this. Your question sounded like that You only want to how get key by value and vice versa. – Grzegorz Piwowarek May 05 '13 at 03:50
  • I need it to be calculated, because I want it to be dynamic and flexible. – Shimmy Weitzhandler May 05 '13 at 03:51
  • How are You going to be using it? Does it have to work for different tonalities? For example '1' in Cmajor is C and '1' in Aminor is A. Transpositions could be easily handled by modulo arithmetic. – Grzegorz Piwowarek May 05 '13 at 03:56
  • 1
    Why would you want it dynamic and flexible? Are you afraid that the theory of sound will be revolutionized? – SimpleVar May 05 '13 at 04:06
  • Now, seriously, this function should be able to analyze complex chords, I want to keep it calculated. – Shimmy Weitzhandler May 05 '13 at 04:09
  • If it could analyze one note then it could do the most complex chords in the world too. – Grzegorz Piwowarek May 05 '13 at 04:10
  • @pivovarit OK, I think I'll give up and go with the `BiDictionary` solution (after I figure out the algorithm of course :P) – Shimmy Weitzhandler May 05 '13 at 04:11
  • @Shimmy You don't have be finding an 'algorithm' for everything. You can just define stuff ;) by the way, You could simply use if-else to check if the number is in the range of notes that have half steps between them. Why bother with switch-case? – Grzegorz Piwowarek May 05 '13 at 04:18
  • If you need to deal with double flats or double sharps, E#, etc. then create a dictionary that maps the diatonic names (C, D, E, F, etc.) to half steps and then subtract one for every flat and add one for every sharp and take the result mod 12. – Michael Scott Asato Cuthbert May 07 '13 at 00:18
0

For the reference, here it is (before using GetNote value should be divided by modulo of 12, to exclude whole octaves):

/// <summary>
/// The values are organized so that the note value + 10 is sharp, -10 is flat, for readability.
/// For instance, Note.C - 10 = Note.CFlat, Note.C + 10 = Note.CSharp.
/// </summary>
public enum Note
{
  Silent = 0,

  CFlat = -9,
  DFlat = -8,
  EFlat = -7,
  FFlat = -6,
  GFlat = -5,
  AFlat = -4,
  BFlat = -3,

  C = 1,
  D = 2,
  E = 3,
  F = 4,
  G = 5,
  A = 6,
  B = 7, 

  CSharp = 10,
  DSharp = 12,
  ESharp = 13,
  FSharp = 14,
  GSharp = 15,
  ASharp = 16,
  BSharp = 17,
}

/// <summary>
/// Returns the note from a chromatic level.
/// For instance: 1 = C, 2 = Db, 6 = F, etc.
/// </summary>
/// <param name="chromaticStep"></param>
public static Note GetNote(int chromaticStep)
{
  if (chromaticStep < 0 || chromaticStep > 12)
    throw new ArgumentOutOfRangeException("chromaticStep",
      "The value must be within the octave range.");

  var diatonicStep = (chromaticStep / 2) + 1;

  //determines whether it's in the upper half of the keyboard layout (> E)
  var isUpperHalf = chromaticStep > 5;
  var isOdd = chromaticStep % 2 != 0;
  var isChromatic = isUpperHalf ? isOdd : !isOdd;

  if (isChromatic)
    diatonicStep += isUpperHalf ? 10 : -10;

  return (Note)diatonicStep;
}

Test:

static void Main(string[] args)
{
  for (int i = 1; i < 13; i++)
     Console.WriteLine("{0}: {1}", i, GetNote(i));
}

Result:

1:      C
2:      DFlat
3:      D
4:      EFlat
5:      E
6:      F
7:      FSharp
8:      G
9:      GSharp
10:     A
11:     ASharp
12:     B
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632