Skip to content

Instantly share code, notes, and snippets.

@jgcoded
Last active July 25, 2018 06:32
Show Gist options
  • Save jgcoded/5a0b83b5e90c1f099ec4b17e008366bb to your computer and use it in GitHub Desktop.
Save jgcoded/5a0b83b5e90c1f099ec4b17e008366bb to your computer and use it in GitHub Desktop.
Code that uses music theory to play scales and make chords
// MusicBeep.cpp : music theory code to make scales and chords
//
#include "stdafx.h"
#include <Windows.h>
#include <cmath>
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <cctype>
using namespace std;
// Equal temperament tuning
enum class note {
C,
Csharp,
D,
Eflat,
E,
F,
Fsharp,
G,
Aflat,
A,
Bflat,
B,
};
enum class intervals {
unison = 0,
minorSecond = 1,
majorSecond = 2,
minorThird = 3,
majorThird = 4,
fourth = 5,
diminishedFifth = 6,
fifth = 7,
minorSixth = 8,
majorSixth = 9,
minorSeventh = 10,
majorSeventh = 11,
octave = 12,
};
constexpr float middleC = 261.63f;
float raiseByInterval(float freq, intervals interval) {
// constexpr float semitoneFactor = 1.0594630944f; 2 to the twelfth root
return freq * std::pow(2.0f, (int)interval / 12.0f);
}
auto intervalFactory(intervals interval) {
return [interval](float freq) { return raiseByInterval(freq, interval); };
}
const auto unison = intervalFactory(intervals::unison);
const auto minorSecond = intervalFactory(intervals::minorSecond);
const auto majorSecond = intervalFactory(intervals::majorSecond);
const auto minorThird = intervalFactory(intervals::minorThird);
const auto majorThird = intervalFactory(intervals::majorThird);
const auto fourth = intervalFactory(intervals::fourth);
const auto diminishedFifth = intervalFactory(intervals::diminishedFifth);
const auto fifth = intervalFactory(intervals::fifth);
const auto minorSixth = intervalFactory(intervals::minorSixth);
const auto majorSixth = intervalFactory(intervals::majorSixth);
const auto minorSeventh = intervalFactory(intervals::minorSeventh);
const auto majorSeventh = intervalFactory(intervals::majorSeventh);
const auto octave = intervalFactory(intervals::octave);
float raiseBySemitone(float freq) {
return minorSecond(freq);
}
float raiseByTone(float freq) {
return majorSecond(freq);
}
vector<float> makeTetrachordPattern(float freq) {
vector<float> result;
result.push_back(freq);
freq = raiseByTone(freq);
result.push_back(freq);
freq = raiseByTone(freq);
result.push_back(freq);
freq = raiseBySemitone(freq);
result.push_back(freq);
return result;
}
vector<float> makeMajorScale(float freq) {
vector<float> result;
// tetrachord pattern
auto tetraChordOne = makeTetrachordPattern(freq);
freq = tetraChordOne.back();
freq = raiseByTone(freq);
auto tetraChordTwo = makeTetrachordPattern(freq);
result.insert(result.end(), tetraChordOne.cbegin(), tetraChordOne.cend());
result.insert(result.end(), tetraChordTwo.cbegin(), tetraChordTwo.cend());
return result;
}
float getSemitonesFromC0(float freq) {
constexpr float C0 = 16.35f;
return 12.0f * log2(freq / C0);
}
int getOctave(float freq) {
return (int)(getSemitonesFromC0(freq) / 12);
}
int getSemitonesFromCx(float freq) {
return (int)getSemitonesFromC0(freq) % 12;
}
note frequencyToNote(float freq) {
auto semitones = getSemitonesFromCx(freq);
return (note)semitones;
}
string noteToString(note note) {
switch (note) {
case note::A: return "A";
case note::Aflat: return "Ab";
case note::B: return "B";
case note::Bflat: return "Bb";
case note::C: return "C";
case note::Csharp: return "C#";
case note::D: return "D";
case note::E: return "E";
case note::Eflat: return "Eb";
case note::F: return "F";
case note::Fsharp: return "F#";
case note::G: return "G";
default:
return "Not Available";
};
}
string frequencyToNoteName(float freq) {
return noteToString(frequencyToNote(freq));
}
void printFrequencyAsNoteName(float freq) {
cout << frequencyToNoteName(freq);
}
void printFrequencyAsNoteNameWithOctave(float freq) {
printFrequencyAsNoteName(freq);
cout << getOctave(freq);
}
void printFrequenciesAsNoteNames(vector<float> freqs) {
for (const auto& freq : freqs) {
printFrequencyAsNoteName(freq);
cout << " ";
}
}
void playFrequencies(vector<float> freqs) {
constexpr int noteDurationMs = 300;
for (const auto& freq : freqs) {
Beep((DWORD)freq, noteDurationMs);
Sleep(noteDurationMs);
}
}
note nameToNote(string root) {
std::transform(root.begin(), root.end(), root.begin(), [](char c) { return std::toupper(c); });
if (root == "A") {
return note::A;
} else if (root == "Ab" || root == "G#") {
return note::Aflat;
} else if (root == "B") {
return note::B;
} else if (root == "Bb" || root == "A#") {
return note::Bflat;
}
else if (root == "C") {
return note::C;
}
else if (root == "C#" || root == "Db") {
return note::Csharp;
}
else if (root == "D") {
return note::D;
}
else if (root == "E") {
return note::E;
}
else if (root == "Eb" || root == "D#") {
return note::Eflat;
}
else if (root == "F") {
return note::F;
}
else if (root == "F#" || root == "Gb") {
return note::Fsharp;
}
else if (root == "G") {
return note::G;
}
throw std::exception("Invalid root note provided as input");
}
float noteToFrequency(note targetNote) {
auto currentNote = note::C;
auto frequency = middleC;
while (currentNote != targetNote) {
frequency = raiseBySemitone(frequency);
currentNote = (note)(((int)currentNote + 1) % (int)note::B);
}
return frequency;
}
float nameToFrequency(string name) {
return noteToFrequency(nameToNote(name));
}
vector<float> makeTriad(float root) {
vector<float> result;
result.push_back(root);
result.push_back(majorThird(root));
result.push_back(fifth(root));
return result;
}
vector<float> makeDominantSeventhChord(float root) {
auto result = makeTriad(root);
result.push_back(minorSeventh(root));
return result;
}
int main(int argc, const char** argv)
{
if (argc < 2) {
cout << "usage: " << argv[0] << " mode" << endl;
return -1;
}
string mode = argv[1];
if (mode == "circle") {
cout << "Circle of fifths: " << endl;
cout << "Major: " << endl;
auto freq = middleC;
do {
printFrequencyAsNoteName(freq);
cout << " ";
freq = fifth(freq);
} while (frequencyToNote(freq) != note::C);
} else if (mode == "scale") {
if (argc < 3) {
cout << "usage" << argv[0] << " mode " << " <root note>" << endl;
return -1;
}
string root = argv[2];
if (root.length() > 2) {
cout << "root note name is too long. Valid values are C, C#, Ab, etc";
}
auto frequency = nameToFrequency(root);
auto scale = makeMajorScale(frequency);
auto i = scale[0];
auto iv = scale[3];
auto v = scale[4];
auto iChord = makeTriad(i);
auto ivChord = makeTriad(iv);
auto vChord = makeTriad(v);
auto dominantSeventh = makeDominantSeventhChord(v);
cout << root << " major scale: " << endl;
printFrequenciesAsNoteNames(scale);
cout << endl;
cout << endl;
cout << "primary chords: " << endl;
cout << "I: ";
printFrequenciesAsNoteNames(iChord);
cout << endl;
cout << endl;
cout << "IV: ";
printFrequenciesAsNoteNames(ivChord);
cout << endl;
cout << endl;
cout << "V: ";
printFrequenciesAsNoteNames(vChord);
cout << endl;
cout << endl;
cout << "Dominant Seventh chord: ";
printFrequencyAsNoteName(v);
cout << "7" << endl;
printFrequenciesAsNoteNames(dominantSeventh);
cout << endl;
playFrequencies(scale);
std::reverse(scale.begin(), scale.end());
playFrequencies(scale);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment