All Projects → thelowsunoverthemoon → mahler.c

thelowsunoverthemoon / mahler.c

Licence: MIT license
Western music theory library in C99

Programming Languages

c
50402 projects - #5 most used programming language

Projects that are alternatives of or similar to mahler.c

Tonal
A functional music theory library for Javascript
Stars: ✭ 2,156 (+16484.62%)
Mutual labels:  scale, music-theory, chords, interval, key-signatures
MusicTheory
Music Theory Library for Java and Android apps
Stars: ✭ 24 (+84.62%)
Mutual labels:  scale, music-theory, chords
Musictheory
Universal music theory library for iOS, iPadOS, macOS, tvOS and watchOS in Swift
Stars: ✭ 262 (+1915.38%)
Mutual labels:  scale, interval
arpeggio
A chord naming app for guitar written in React.
Stars: ✭ 49 (+276.92%)
Mutual labels:  music-theory, chords
Scribbletune
Create music with JavaScript
Stars: ✭ 3,509 (+26892.31%)
Mutual labels:  scale, chords
chords
Text-based chord progression editor
Stars: ✭ 25 (+92.31%)
Mutual labels:  music-theory, chords
dradis-docker
A Docker image with Dradis: A collaboration and reporting platform for IT security experts.
Stars: ✭ 13 (+0%)
Mutual labels:  minimal
dlock
Interval Lock
Stars: ✭ 19 (+46.15%)
Mutual labels:  interval
candlestick-convert
[NPM] OHLCV Candlestick Batcher/Converter
Stars: ✭ 39 (+200%)
Mutual labels:  interval
oceanbase
OceanBase is an enterprise distributed relational database with high availability, high performance, horizontal scalability, and compatibility with SQL standards.
Stars: ✭ 4,466 (+34253.85%)
Mutual labels:  scale
gatsby-portfolio
Portfolio / Personal Website - Built with Gatsby.js and Published at konstantin.digital
Stars: ✭ 23 (+76.92%)
Mutual labels:  minimal
Ephesus
Ephesus is a minimalist Jekyll theme, designed for personal blog use.
Stars: ✭ 40 (+207.69%)
Mutual labels:  minimal
goide
Docker run to a sane vim-go setup
Stars: ✭ 19 (+46.15%)
Mutual labels:  minimal
the-roots-home
I am not root, this is a hugo theme.
Stars: ✭ 29 (+123.08%)
Mutual labels:  minimal
kolorist
A tiny utility to colorize stdin/stdout
Stars: ✭ 160 (+1130.77%)
Mutual labels:  minimal
pure
Beautifully crafted minimalistic loading animations.
Stars: ✭ 14 (+7.69%)
Mutual labels:  minimal
AIBud
An experimental CreateML project for predicting playing musical key and scale in realtime
Stars: ✭ 18 (+38.46%)
Mutual labels:  music-theory
siimple
The minimal and themeable CSS toolkit for flat and clean designs
Stars: ✭ 525 (+3938.46%)
Mutual labels:  minimal
pro-writer
Minimal yet Pro Writer 🚀
Stars: ✭ 22 (+69.23%)
Mutual labels:  minimal
minimum-viable-startpage
A very simple startpage to replace new tab in a browser of choice.
Stars: ✭ 61 (+369.23%)
Mutual labels:  minimal

mahler.c

A simple and easy-to-use library for Western music theory in pure C99

Features

  • Small & easy to compile
  • Interval, Chord, Scale, and Key Signature functions
  • No Memory Allocation happening under the hood
  • Supports Theoretical Keys (ie Fb+)
  • No Accidental Limit (ie G 20th sharp)
  • Enharmonically Correct (ie minor 6th of D is Bb, not A#)

Who's Mahler?

Gustav Mahler is one of my favourite composers; if you like the emotions of Wagner, and the ideas of Stravinsky, Mahler is the perfect middle ground! All his works have a flair of modernism that gives his emotional works a unique touch. You should definitely check him out, especially his Symphony No.5 in C# Minor, The Song of the Earth, and Symphony No.6 in A Minor.

Example

Here's an example that creates the C4 Blues Scale, ascending:

struct Note notes[7];
struct Scale scale = getScale(
    (struct Note) {MAHLER_C, MAHLER_NONE, 4}, &MAHLER_BLUES_SCALE, notes, MAHLER_ASCEND
);

And if you want to print it:

char print[MAHLER_DISP_LEN];
for (int i = 0; i < scale.size; i++) {
    puts(printNote(scale.notes[i], print, MAHLER_DISP_LEN));
}

Documentation

Table of Contents

🟥 Enumerators & Macros 🟥

#define MAHLER_DISP_LEN 8

This is the default print size you can use for printNote(). The rationale is note (1) + max acci (<= 4) + number (<= 99) + null terminating (1).

enum MahlerNote {
    MAHLER_C, MAHLER_D, MAHLER_E, MAHLER_F, MAHLER_G, MAHLER_A, MAHLER_B
};

note member struct Note, and represents the "root" note (no accidentals).

enum MahlerQuality {
    MAHLER_MINOR = -1, MAHLER_MAJOR = 0, MAHLER_AUGMENTED = 1, MAHLER_DIMINISHED = -2, MAHLER_PERFECT = 3
};

quality member of struct Interval. Self-explanatory.

enum MahlerAcci {
    MAHLER_DBFLAT = -2, MAHLER_FLAT = -1, MAHLER_NONE = 0, MAHLER_SHARP = 1, MAHLER_DBSHARP = 2
};

acci member of struct Note. Also self-explanatory. All functions support an infinite range of accidentals (ie, 30th sharp). Only thing holding you back is the size of enum Accidental.

enum MahlerScaleType {
    MAHLER_ASCEND, MAHLER_DESCEND, MAHLER_FULL
};

mode parameter of getScale(). Determines whether it is ascending, descending, or full (both). Scales include 8th degree, and in SCALE_FULL it is doubled.

enum MahlerKeyType {
    MAHLER_MAJOR_KEY, MAHLER_MINOR_KEY
};

type parameter of getKeySig() and returnKeySig(). Determines type of key signature.

enum MahlerChordType {
     MAHLER_TRIAD = 3, MAHLER_SEVENTH_CHORD = 4, MAHLER_NINTH_CHORD = 5, MAHLER_ELEVENTH_CHORD = 6
};

size parameter of getKeyChord. Determines size of returned chord.

enum MahlerDegree {
    MAHLER_TONIC = 1, MAHLER_SUPERTONIC = 2, MAHLER_MEDIANT = 3, MAHLER_SUBDOMINANT = 4,
    MAHLER_DOMINANT = 5, MAHLER_SUBMEDIANT = 6, MAHLER_LEADING_TONE = 7, MAHLER_SUBTONIC = 8
};

enum MahlerNumeral {
    MAHLER_I = 1, MAHLER_II = 2, MAHLER_III = 3, MAHLER_IV = 4,
    MAHLER_V = 5, MAHLER_VI = 6, MAHLER_VII = 7, MAHLER_VIII = 8
};

enum MahlerMode {
    MAHLER_IONIAN = 1, MAHLER_DORIAN = 2, MAHLER_PHRYGIAN = 3, MAHLER_LYDIAN = 4,
    MAHLER_MIXOLYDIAN = 5, MAHLER_AEOLIAN = 6, MAHLER_LOCRIAN = 7
};

These are generals enums that can be used where convenient in place of numbers. For example, one could use them as the index parameter of getKeyChord.

enum MahlerError {
    MAHLER_ERROR_NONE,
    MAHLER_ERROR_INVALID_QUAL, MAHLER_ERROR_INVALID_INTER, MAHLER_ERROR_INVALID_INVERSION, MAHLER_ERROR_INVALID_PRINT_NOTE,
    MAHLER_ERROR_OVERFLOW_PRINT_NOTE, MAHLER_ERROR_OVERFLOW_SCALE_RETURN, MAHLER_ERROR_OVERFLOW_CHORD_RETURN
};

For reference only. See Error Handling section down below.

🟦 Predefined 🟦

Predefined Scales

extern struct ScaleBase const MAHLER_MAJOR_SCALE;          // Major Scale
extern struct ScaleBase const MAHLER_NATURAL_MIN_SCALE;    // Natural Minor Scale
extern struct ScaleBase const MAHLER_HARMONIC_MIN_SCALE;   // Harmonic Minor Scale
extern struct ScaleBase const MAHLER_MELODIC_MIN_SCALE;    // Melodic Minor Scale
extern struct ScaleBase const MAHLER_PENTATONIC_MAJ_SCALE; // Major Pentatonic Scale
extern struct ScaleBase const MAHLER_PENTATONIC_MIN_SCALE; // Minor Pentatonic Scale
extern struct ScaleBase const MAHLER_BLUES_SCALE;          // Blues Scale (hexatonic)
extern struct ScaleBase const MAHLER_WHOLE_TONE_SCALE;     // Whole Tone Scale
extern struct ScaleBase const MAHLER_OCTATONIC_HALF_SCALE; // Octatonic Scale (starting with half tone)
extern struct ScaleBase const MAHLER_OCTATONIC_WHOLE_SCALE;// Octatonic Scale (starting with whole tone)

Predefined Chords

extern struct ChordBase const MAHLER_MAJOR_TRIAD;          // Major Triad
extern struct ChordBase const MAHLER_MINOR_TRIAD;          // Minor Triad
extern struct ChordBase const MAHLER_AUGMENTED_TRIAD;      // Augmented Triad
extern struct ChordBase const MAHLER_DIMINISHED_TRIAD;     // Diminished Triad
extern struct ChordBase const MAHLER_DIMINISHED_7;         // Diminished 7th
extern struct ChordBase const MAHLER_HALF_DIMINISHED_7;    // Half Diminished 7th
extern struct ChordBase const MAHLER_MINOR_7;              // Minor 7th
extern struct ChordBase const MAHLER_MAJOR_7;              // Major 7th
extern struct ChordBase const MAHLER_DOMINANT_7;           // Dominant 7th

Predefined Chord List

{
    &MAHLER_MAJOR_TRIAD,
    &MAHLER_MINOR_TRIAD,
    &MAHLER_AUGMENTED_TRIAD,
    &MAHLER_DIMINISHED_TRIAD,
    &MAHLER_DIMINISHED_7,
    &MAHLER_DOMINANT_7
};

Predefined Scale List

{
    &MAHLER_MAJOR_SCALE,
    &MAHLER_NATURAL_MIN_SCALE,
    &MAHLER_HARMONIC_MIN_SCALE,
    &MAHLER_MELODIC_MIN_SCALE
};

🟧 Structures 🟧

struct Note {
    enum MahlerNote note;
    int             acci;
    int             pitch;
};

The building block of music theory. pitch is the octave the note resides in.

struct Interval {
    int                inter;
    enum MahlerQuality quality;
};

Self explanatory.

struct Chord {
    int const                size;
    int                      inversion;
    struct Note const* const base;
    struct Note* const       notes;
};

You must provide two arrays of struct Note : base is for the root inversion chord (ie G7 is G B D F) and notes is for the current inversion (ie B D F G) specified in inversion.

struct Scale {
    int const                  size;
    enum MahlerScaleType const type;
    struct Note* const         notes;
};

Self-explanatory.

struct KeySig {
    enum MahlerKeyType const type;
    int const                alter; // alteration
    int const                size;
    struct Note const        key;
    struct Note              notes[7];
};

alter is the sum of the accidentals in the key (ie, G+ is 1 and G- is -2). size is the number of accidentals in the key (ie, G+ is 1 and G- is 2). key is the key note. The pitches in notes are NOT guaranteed to be 0.

struct ChordResult {
    struct Note             key;
    struct ChordBase const* chord;
};

struct ScaleResult {
    struct Note             key;
    struct ScaleBase const* scale;
};

Used for results of returnChord and returnScale, respectively.

struct ChordBase {
    char const*            name;
    int const              size;
    struct Interval const* steps;
};

struct ScaleBase {
    char const*            name;
    int const              size;
    struct Interval const* steps;
};

These are the "types" of Chords/Scales to be used. A number of common types have been pre-defined, but you make make your own if you wish (see Predefined). steps defines the intervals between each note (ie, G -> B -> D is a major 3rd, then a minor 3rd). size for struct ScaleBase includes the octave (ie, a major scale is size 8)

struct ChordList {
    struct ChordBase const** chordPos;
    size_t const             chordNum;
    struct Note* restrict    chordBase;
    struct Note* restrict    chordNotes;
};

struct ScaleList {
    struct ScaleBase const** scalePos;
    size_t const             scaleNum;
    struct Note*             scaleArray;
};

These are used for custom lists when checking for chords/scales in returnScale() and returnChord(). The *Pos contain pointers to an array of bases, and *Num must contain the number of pointers inside. The arrays of struct Note must be big enough to hold the largest chord/scale. In terms of returnScale(), it must be big enough to hold the largest scale in SCALE_ASCEND mode.

🟨 Interval Functions 🟨

struct Note getInter(struct Note note, struct Interval interval);

getInter accepts all intervals (both simple and compound). Returns the destination note of interval starting from note. If the given interval is an invalid quality (ie, non-perfect intervals with perfect quality, or perfect intervals with major or minor quality), then the last error is set to MAHLER_ERROR_INVALID_QUAL.

struct Interval returnInter(struct Note notea, struct Note noteb);

returnInter does the opposite. Given two notes, it will return the interval between them. If the resulting interval is invalid (wrong quality), then the last error is set to MAHLER_ERROR_INVALID_INTER.

Example

struct Note note = {MAHLER_B, MAHLER_DBFLAT, 4};
struct Interval inter = {4, MAHLER_AUGMENTED};

printf("Augmented 4th of B double flat is %s",
    printNote(getInter(note, inter), (char[MAHLER_DISP_LEN]) {0}, MAHLER_DISP_LEN)
);

Result

Augmented 4th of B double flat is Eb5.

🟩 Chord Functions 🟩

struct Chord getChord(struct Note root, struct ChordBase const* type, struct Note* restrict base, struct Note* restrict notes);

Returns a struct Chord with root root and type type. You must provide two arrays of struct Note : base is for the root inversion chord (ie G7 is G B D F) and notes is for the current inversion (ie B D F G) specified in inversion.

void returnChord(struct Note const notes[], size_t noteNum, struct ChordResult list[], size_t listMax, struct ChordList const* custom, bool enharmonic);

This function populates list with the potential chords containing each note in notes . noteNum is the number of entries in notes, while listMax - 1 is the maximum number of entries to write to. The reason for -1 is because the last struct ChordBase is set empty as a looping sentinel. The pitch of each struct ChordResult note is 0. Defining custom will check for chords specified in struct ChordList. Set to NULL is you would like to use the predefined chord list (see Predefined). useEnharmonic determines whether enharmonic equivalents are used (ie, Bb+ triad is also A#+ triad). If there are more possible chords than listMax - 1, the last error is set to MAHLER_ERROR_OVERFLOW_CHORD_RETURN. This function tests for chords up to one accidental (ie, flat, natural, and sharp).

void invertChord(struct Chord* chord, int inversion);

This function inverts the notes member of chord to the inversionth inversion. base is left unaltered. An inversion of 0 is considered the root inversion. Any invalid inversions will set the last error to MAHLER_ERROR_INVALID_INVERSION.

struct Chord getKeyChord(struct KeySig const* key, size_t index, size_t size, struct Note* restrict base, struct Note* restrict notes);

Returns a struct Chord starting on indexth note of key (ie, index 3 of D+ would be F#) of size size (ie, 3 would return a triad). base and notes are identical to getChord. See general enums of Enumerators & Macros.

Example

struct Note base[4];
struct Note notes[4];
struct Chord chord = getChord(
    (struct Note) {MAHLER_D, MAHLER_NONE, 3},
    &MAHLER_DOMINANT_7,
    base, notes
);

for (size_t i = 0; i < chord.size; i++) {
    invertChord(&chord, i);
    
    char disp[MAHLER_DISP_LEN];
    for (size_t j = 0; j < chord.size; j++) {
        printf("%s ", printNote(chord.notes[j], disp, MAHLER_DISP_LEN));
    }
    putchar('\n');
}

Result

D3 F#3 A3 C4
F#3 A3 C4 D4
A3 C4 D4 F#4
C4 D4 F#4 A4

🟦 Scale Functions 🟦

struct Scale getScale(struct Note start, const struct ScaleBase* type, struct Note notes[], enum ScaleType mode);

Returns a type scale starting on start. notes contains the notes of the scale, hence the size must be >= the size member of type. As well, a mode of MAHLER_FULL doubles the size requirement (ie, if it was 8, MAHLER_FULL would be 16).

void returnScale(struct Note const notes[], size_t noteNum, struct ScaleResult list[], size_t listMax, struct ScaleList const* custom, bool enharmonic);

Identical to returnChord(), but for scales.

struct Scale getKeyScale(struct KeySig const* key, size_t index, enum MahlerScaleType mode, struct ScaleBase const* type, struct Note* notes);

Returns a struct Scale starting on indexth note of key (ie, index 3 of D+ would be F#). In other words, it returns the indexth mode of a given key (ie, index 2 of C+ would be C dorian). See general enums of Enumerators & Macros.

Example

struct ScaleResult list[5];
returnScale(
    (struct Note[]) {
        {MAHLER_A, MAHLER_FLAT, 0},
        {MAHLER_C, MAHLER_FLAT, 0},
        {MAHLER_G, MAHLER_NONE, 0}
    },
    3, list, sizeof(list) / sizeof(*list), NULL, true
);

char disp[MAHLER_DISP_LEN];
for (size_t i = 0; list[i].scale; i++) {
    printf("%s %s\n",
        printNote(list[i].key, disp, MAHLER_DISP_LEN), list[i].scale->name
    );
}

Result

Ab0 Harmonic Minor
G#0 Harmonic Minor
Ab0 Melodic Minor
G#0 Melodic Minor

🟧 Key Signature Functions 🟧

struct KeySig getKeySig(struct Note key, enum MahlerKeyType type);

Returns a struct KeySig. All Key Signature functions support theoritical keys (ie D#+).

struct KeySig returnKeySig(char const* str, enum MahlerKeyType type);

Returns a struct KeySig based on str, which contains the accidentals stringified. For example, "###" with MAHLER_MAJOR_KEY would return A+. Accepted characters are # (sharp), x (double sharp), and b (flat). Any other characters are ignored. Equivalent expressions are accepted (ie "x#" -> G+).

struct KeySig getKeyRelative(struct KeySig const* key);

Returns the relative major/minor of the given key.

int queryAcci(struct KeySig const* key, enum MahlerNote note);

Returns the accidental of the given note based on key. Note that this is not a struct Note but the "base" note of type enum MahlerNote.

Example

struct KeySig key = getKeySig(
    (struct Note) {MAHLER_G, MAHLER_NONE, 0},
    MAHLER_MINOR_KEY
);
    
char disp[MAHLER_DISP_LEN];
for (size_t i = 0; i < key.size; i++) {
    printf("%s ", printNote(key.notes[i], disp, MAHLER_DISP_LEN));
}

Result

Bb0 Eb0

🟪 Misc Functions 🟪

char* printNote(struct Note const note, char buf[], size_t size);

This returns the buffer with note in text up to 4 accidentals (ie, bbbb -> ####). If acci exceeds that range or the note member is invalid, the last error is set to MAHLER_ERROR_INVALID_PRINT_NOTE. If the given buffer is not large enough, the last error is set to MAHLER_ERROR_OVERFLOW_PRINT_NOTE.

bool isEnharmonic(struct Note notea, struct Note noteb);

This returns true if enharmonic, false if not. Identical notes are considered enharmonic.

Example

struct Note noteA = {MAHLER_C, MAHLER_FLAT, 4};
struct Note noteB = {MAHLER_B, MAHLER_NONE, 3};

if (isEnharmonic (noteA, noteB)) {
    puts("Enharmonic!");
}

Result

Enharmonic!

🟫 Error Handling 🟫

If a function encounters one of the defined errors, it will return an zeroed struct if applicable, or an empty string in the case of printNote(). You can then query the error through

char* getMahlerError(void);

which returns a string containing details of the last error. Read each function blurb for their specific errors.

To Do

  • Add Key Signature Functions
  • Octatonic Scale comment
  • Add More Preset Scales, add option to include own in list in return functions
  • Cadences?...
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].