// // Programmer: Craig Stuart Sapp // Creation Date: Mon Jun 8 00:38:46 PDT 1998 // Last Modified: Tue Jun 23 14:00:23 PDT 1998 // Last Modified: Fri May 5 12:18:44 PDT 2000 (added kernToMidiNoteNumber()) // Last Modified: Tue Jun 6 13:51:55 PDT 2000 (durationToKernRhythm() improved) // Filename: ...humdrum++/src/Convert.cpp // Syntax: C++ // $Smake: cc -Wall -I../include -g -c %f && rm %b.o // #include "Convert.h" #include "HumdrumEnumerations.h" #include #include #include #include #ifdef VISUAL #include /* for Windows 95 */ #else #include #endif EnumerationExclusiveInterpretation Convert::exint; EnumerationChordQualityType Convert::chordType; EnumerationChordQualityInversion Convert::chordInversion; EnumerationChordQualityRoot Convert::kernPitchClass; EnumerationMusePitchClass Convert::musePitchClass; EnumerationInterval Convert::intervalNames; /////////////////////////////////////////////////////////////////////////// // // conversions dealing with humdrum data // /////////////////////////////////////////////////////////////////////////// // // conversions dealing with **kern data // ////////////////////////////// // // Convert::kernToMidiNoteNumber -- -1 means a rest or other negative // number; otherwise the notes should end up in the range from 0 to 127. // int Convert::kernToMidiNoteNumber(const char* aKernString) { int base40 = Convert::kernToBase40(aKernString); if (base40 < 0) { return -1; } int octave = base40 / 40; int pitch = 0; switch (base40 % 40) { case 0: pitch = -2; break; // C-- case 1: pitch = -1; break; // C- case 2: pitch = 0; break; // C case 3: pitch = 1; break; // C# case 4: pitch = 2; break; // C## case 6: pitch = 0; break; // D-- case 7: pitch = 1; break; // D- case 8: pitch = 2; break; // D case 9: pitch = 3; break; // D# case 10: pitch = 4; break; // D## case 12: pitch = 2; break; // E-- case 13: pitch = 3; break; // E- case 14: pitch = 4; break; // E case 15: pitch = 5; break; // E# case 16: pitch = 6; break; // E## case 17: pitch = 3; break; // F-- case 18: pitch = 4; break; // F- case 19: pitch = 5; break; // F case 20: pitch = 6; break; // F# case 21: pitch = 7; break; // F## case 23: pitch = 5; break; // G-- case 24: pitch = 6; break; // G- case 25: pitch = 7; break; // G case 26: pitch = 8; break; // G# case 27: pitch = 9; break; // G## case 29: pitch = 7; break; // A-- case 30: pitch = 8; break; // A- case 31: pitch = 9; break; // A case 32: pitch = 10; break; // A# case 33: pitch = 11; break; // A## case 35: pitch = 9; break; // B-- case 36: pitch = 10; break; // B- case 37: pitch = 11; break; // B case 38: pitch = 12; break; // B# case 39: pitch = 13; break; // B## default: cout << "Pitch Unknown: " << base40 % 40 << endl; pitch = -1000; } return octave * 12 + pitch + 10; } ////////////////////////////// // // Convert::durationToKernRhythm -- not allowed to have more than // two rhythmic dots // default value: timebase = 1; // char* Convert::durationToKernRhythm(char* output, double input, int timebase) { strstream temp; double testinput = input; double basic = 4.0 / input * timebase; double diff = basic - (int)basic; if (diff > 0.998) { diff = 1.0 - diff; basic += 0.002; } if (diff < 0.002) { temp << (int)basic << ends; strcpy(output, temp.str()); } else { testinput = input / 3.0 * 2.0; basic = 4.0 / testinput; diff = basic - (int)basic; if (diff < 0.002) { temp << (int)basic << ends; strcpy(output, temp.str()); strcat(output, "."); } else { testinput = input / 7.0 * 4.0; basic = 4.0 / testinput; diff = basic - (int)basic; if (diff < 0.002) { temp << (int)basic << ends; strcpy(output, temp.str()); strcat(output, ".."); } else { cerr << "Error: Convert::durationToKernRhythm choked on the " << "duration: " << input << endl; exit(1); } } } return output; } ////////////////////////////// // // Convert::kernToDuration -- returns the kern rhythm's duration, using // 1.0 as the duration of a quarter note (rhythm=4). // double Convert::kernToDuration(const char* aKernString) { int index = 0; while (aKernString[index] != '\0' && !isdigit(aKernString[index])) { index++; } if (aKernString[index] == '\0') { // no rhythm data found return 0; } // should now be at start of kern rhythm int orhythm = 0; while (aKernString[index] != '\0' && isdigit(aKernString[index])) { orhythm *= 10; orhythm += aKernString[index] - '0'; index++; } // check for dots to modify rhythm int dotcount = 0; while (aKernString[index] != '\0' && aKernString[index] == '.') { dotcount++; index++; } // now know everything to create a duration double oduration; if (orhythm == 0) { oduration = 8.0; } else { oduration = 4.0 / orhythm; } double duration = oduration; for (int i=0; i notes; notes = chordQualityToNoteSet(aQuality); return notes[aQuality.getInversion()] % 40; } ////////////////////////////// // // Convert::chordQualityToNoteSet // Collection Convert::chordQualityToNoteSet(const ChordQuality& aQuality) { Collection output; output.setSize(0); output.allowGrowth(); output[0] = 0; switch (aQuality.getType()) { case E_chord_note: break; case E_chord_incmin: output[1] = 11; break; case E_chord_incmaj: output[1] = 12; break; case E_chord_secsev: case E_chord_dim: output[1] = 11; output[2] = 22; break; case E_chord_min: output[1] = 11; output[2] = 23; break; case E_chord_neopol: case E_chord_secdom: case E_chord_maj: output[1] = 12; output[2] = 23; break; case E_chord_aug: output[1] = 12; output[2] = 24; break; case E_chord_secsevo: case E_chord_fullydim: output[1] = 11; output[2] = 22; output[3] = 33; break; case E_chord_secsevc: case E_chord_halfdim: output[1] = 11; output[2] = 22; output[3] = 34; break; case E_chord_minmin: output[1] = 11; output[2] = 23; output[3] = 34; break; case E_chord_minmaj: output[1] = 11; output[2] = 23; output[3] = 35; break; case E_chord_secdomsev: case E_chord_domsev: output[1] = 12; output[2] = 23; output[3] = 34; break; case E_chord_majmaj: output[1] = 12; output[2] = 23; output[3] = 35; break; case E_chord_frensix: output[1] = 12; output[2] = 18; output[3] = 30; break; case E_chord_germsix: output[1] = 12; output[2] = 23; output[3] = 30; break; case E_chord_italsix: output[1] = 12; output[2] = 30; break; default: cerr << "Error: unknown type of chord quality: " << aQuality.getType() << endl; } // add bass offset for (int i=0; i& aSet) { ChordQuality output; Collection newnotes(aSet.getSize()); int i, k; // remove rests and find the lowest note int index = 0; for (i=0; i E_root_rest) { newnotes[index] = aSet[i]; index++; } } if (index == 0) { output.setType(E_chord_rest); output.setInversion(E_blank); output.setRoot(E_blank); return output; } else { newnotes.setSize(index); } qsort(newnotes.getBase(), newnotes.getSize(), sizeof(int), intcompare); int basenote = BASE40_PITCHCLASS(newnotes[0]); // remove octave values for (i=0; i 12 || octave < -1) { cerr << "Error: unresonable octave value: " << octave << endl; exit(1); } int chroma = aPitch % 40; strcpy(output, kernPitchClass.getName(chroma)); if (octave >= 4) { output[0] = tolower(output[0]); } else { output[0] = toupper(output[0]); } int repeat = 0; switch (octave) { case 4: repeat = 0; break; case 5: repeat = 1; break; case 6: repeat = 2; break; case 7: repeat = 3; break; case 8: repeat = 4; break; case 9: repeat = 5; break; case 3: repeat = 0; break; case 2: repeat = 1; break; case 1: repeat = 2; break; case 0: repeat = 3; break; case -1: repeat = 4; break; default: cerr << "Error: unknown octave value: " << octave << endl; exit(1); } if (repeat == 0) { return output; } int length = strlen(output); output[length + repeat] = '\0'; int i; for (i=length-1; i>=0; i--) { output[i+repeat] = output[i]; } for (i=0; i= 0) { return absolute % 40; } else { return absolute; } } ////////////////////////////// // // kernNoteToBase40 -- returns the absolute base 40 pitch number of // a kern pitch. Returns -1 on error or null token. // int Convert::kernNoteToBase40(const char* name) { int output; if (name[0] == '.') { return -1; } switch (name[0]) { case 'a': output = 4 * 40 + E_root_a; break; case 'b': output = 4 * 40 + E_root_b; break; case 'c': output = 4 * 40 + E_root_c; break; case 'd': output = 4 * 40 + E_root_d; break; case 'e': output = 4 * 40 + E_root_e; break; case 'f': output = 4 * 40 + E_root_f; break; case 'g': output = 4 * 40 + E_root_g; break; case 'A': output = 3 * 40 + E_root_a; break; case 'B': output = 3 * 40 + E_root_b; break; case 'C': output = 3 * 40 + E_root_c; break; case 'D': output = 3 * 40 + E_root_d; break; case 'E': output = 3 * 40 + E_root_e; break; case 'F': output = 3 * 40 + E_root_f; break; case 'G': output = 3 * 40 + E_root_g; break; case 'R': return E_root_rest; break; default: return -1; } int octave=0; while (name[octave] != '\0' && name[octave] == name[0]) { octave++; } if (islower(name[0])) { output += (octave - 1) * 40; } else { output -= (octave - 1) * 40; } // check for first accidental sign int accidental = octave; if (name[accidental] != '\0' && name[accidental] == '-') { output--; } else if (name[accidental] != '\0' && name[accidental] == '#') { output++; } // chek for second accidental sign if (name[accidental] == '\0') { ; // nothing } else { // check for double sharp or double flat accidental++; if (name[accidental] != '\0' && name[accidental] == '-') { output--; } else if (name[accidental] != '\0' && name[accidental] == '#') { output++; } } if (output >= 0) { return output; } else { cerr << "Error: pitch \"" << name << "\" is too low." << endl; exit(1); } } ////////////////////////////// // // Convert::keyToScaleDegrees // Collection Convert::keyToScaleDegrees(int aKey, int aMode) { Collection output; switch (aMode) { case E_mode_major: case E_mode_ionian: output.setSize(7); output[0] = aKey; output[1] = aKey + E_base40_maj2; output[2] = aKey + E_base40_maj3; output[3] = aKey + E_base40_per4; output[4] = aKey + E_base40_per5; output[5] = aKey + E_base40_maj6; output[6] = aKey + E_base40_maj7; break; case E_mode_minor: case E_mode_aeolian: output[0] = aKey; output[1] = aKey + E_base40_maj2; output[2] = aKey + E_base40_min3; output[3] = aKey + E_base40_per4; output[4] = aKey + E_base40_per5; output[5] = aKey + E_base40_min6; output[6] = aKey + E_base40_min7; break; case E_mode_harm_minor: output[0] = aKey; output[1] = aKey + E_base40_maj2; output[2] = aKey + E_base40_min3; output[3] = aKey + E_base40_per4; output[4] = aKey + E_base40_per5; output[5] = aKey + E_base40_min6; output[6] = aKey + E_base40_maj7; break; default: cerr << "unknown mode: " << aMode << endl; } // keep the pitches in the primary octave for (int i=0; i= 0 && !isdigit(temp[i])) { i--; } if (i <= 0) { cerr << "Error: could not find octave in string: " << pitchString << endl; exit(1); } octave = temp[i] - '0'; temp[i] = '\0'; return musePitchClass.getValue(temp) + 40 * (octave); } /////////////////////////////////////////////////////////////////////////// // // Protected functions // ////////////////////////////// // // calculateInversion -- // int Convert::calculateInversion(int aType, int bassNote, int root) { if (aType == E_chord_note) { return -1; } ChordQuality cq(aType, 0, root); Collection notes; notes = chordQualityToNoteSet(cq); int i; for (i=0; i& aSet) { int output; Collection inval(aSet.getSize()-1); for (int i=0; i *((int*)b)) { return 1; } else { return 0; } } ////////////////////////////// // // Convert::rotatechord -- // void Convert::rotatechord(Collection& aChord) { int temp = aChord[0]; for (int i=1; i