mirror of
https://github.com/sshlien/abcmidi.git
synced 2026-02-04 12:48:11 +00:00
2020.10.19
This commit is contained in:
361
music_utils.c
Normal file
361
music_utils.c
Normal file
@@ -0,0 +1,361 @@
|
||||
/* music_utils.c
|
||||
*
|
||||
* Copyright James Allwright 2020
|
||||
*
|
||||
* This file provides basic functions for manipulating music
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "music_utils.h"
|
||||
|
||||
typedef struct clef_item
|
||||
{
|
||||
char *name;
|
||||
basic_cleftype_t basic_clef;
|
||||
int staveline;
|
||||
int octave_offset;
|
||||
} clef_item_t;
|
||||
|
||||
/* entries are name, basic_clef, staveline, octave_offset */
|
||||
static const clef_item_t clef_conversion_table[] = {
|
||||
{"treble", basic_clef_treble, 2, 0},
|
||||
{"alto", basic_clef_alto, 3, 0},
|
||||
{"bass", basic_clef_bass, 4, 0},
|
||||
{"soprano", basic_clef_alto, 1, 0},
|
||||
{"mezzosoprano", basic_clef_alto, 2, 0},
|
||||
{"tenor", basic_clef_alto, 4, 0},
|
||||
{"baritone", basic_clef_bass, 0, 0},
|
||||
};
|
||||
#define NUM_CLEFS (sizeof(clef_conversion_table)/sizeof(clef_item_t))
|
||||
|
||||
/* The following 3 are not really clefs, but abc standard
|
||||
* 2.2 allows them. We treat them as treble clef and only
|
||||
* allow them after clef= . This ensures that K:none is not
|
||||
* interpreted as a clef instead of a key signature.
|
||||
*
|
||||
* the clef defines how a pitch value maps to a y position
|
||||
* on the stave. If there is a clef of "none", then you don't
|
||||
* know where to put the notes!
|
||||
*/
|
||||
static const clef_item_t odd_clef_conversion_table[] = {
|
||||
{"auto", basic_clef_treble, 2, 0},
|
||||
{"perc", basic_clef_treble, 2, 0},
|
||||
{"none", basic_clef_treble, 2, 0}
|
||||
};
|
||||
#define NUM_ODD_CLEFS (sizeof(odd_clef_conversion_table)/sizeof(clef_item_t))
|
||||
|
||||
|
||||
/* array giving notes in the order used by key_acc array
|
||||
* This can be used to implement a lookup function which is
|
||||
* the reverse of note_index().
|
||||
*/
|
||||
const char note_array[] = "cdefgab";
|
||||
|
||||
/* These are musical modes. Each specifies a scale of notes starting on a
|
||||
* particular tonic note. Effectively the notes are the same as used in a
|
||||
* major scale, but the starting note is different. This means that each
|
||||
* mode can be notated using the standard key signatures used for major
|
||||
* keys. The abc standard specifies that only the first 3 characters of
|
||||
* the mode are significant, and further that "minor" can be abbreviated
|
||||
* as "m" and "major" omitted altogether. The full names are:
|
||||
* Major, Minor, Aeolian, Locrian, Ionian, Dorian, Phyrgian, Lydian and
|
||||
* Mixolydian. In addition, we have "exp" short for "explicit" to indicate
|
||||
* that arbitrary accidentals can be applied to each stave line in the
|
||||
* key signature and "" the empty string to represent "major" where this
|
||||
* is inferred as the default value rather than being supplied.
|
||||
*/
|
||||
const char *mode[12] = { "maj", "min", "m",
|
||||
"aeo", "loc", "ion", "dor", "phr", "lyd", "mix", "exp", ""
|
||||
};
|
||||
|
||||
/* This is a table for finding the sharps/flats representation of
|
||||
* a key signature for a given mode.
|
||||
* Suppose we want to find the sharps/flats representation for
|
||||
* K:GDor
|
||||
* If we know the major mode key K:G is 1 sharp, and have the index of the
|
||||
* mode we want within the mode table, we can work out the new key
|
||||
* signature as follows:
|
||||
*
|
||||
* Original major mode key signature +1 (1 sharp)
|
||||
* Desired new mode Dorian is at position 6 in table
|
||||
* modeshift[6] is -2.
|
||||
* new key signature is 1 - 2 = -1 (1 flat)
|
||||
* GDor is 1 flat.
|
||||
*/
|
||||
const int modeshift[12] = { 0, -3, -3, -3, -5, 0, -2, -4, 1, -1, 0, 0 };
|
||||
|
||||
/* convert a note a-g to an index into the key signature arrays
|
||||
* src.key_acc, target.key_acc, src_key_mult, target_key_mult
|
||||
*/
|
||||
noteletter_t note_index (char note_ch)
|
||||
{
|
||||
|
||||
switch (note_ch) {
|
||||
case 'c':
|
||||
case 'C':
|
||||
return note_c;
|
||||
case 'd':
|
||||
case 'D':
|
||||
return note_d;
|
||||
case 'e':
|
||||
case 'E':
|
||||
return note_e;
|
||||
case 'f':
|
||||
case 'F':
|
||||
return note_f;
|
||||
case 'g':
|
||||
case 'G':
|
||||
return note_g;
|
||||
case 'a':
|
||||
case 'A':
|
||||
return note_a;
|
||||
case 'b':
|
||||
case 'B':
|
||||
return note_b;
|
||||
default:
|
||||
printf ("Internal error: note_index called with bad value %c\n", note_ch);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
/* convert a letter a - g into a note value in semitones */
|
||||
int semitone_value_for_note (noteletter_t note)
|
||||
{
|
||||
switch (note) {
|
||||
case note_c:
|
||||
return 0;
|
||||
case note_d:
|
||||
return 2;
|
||||
case note_e:
|
||||
return 4;
|
||||
case note_f:
|
||||
return 5;
|
||||
case note_g:
|
||||
return 7;
|
||||
case note_a:
|
||||
return 9;
|
||||
case note_b:
|
||||
return 11;
|
||||
default:
|
||||
printf ("Internal error: Unexpected note %d\n", note);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert an accidental indicator into a semitone shift */
|
||||
int semitone_shift_for_acc (char acc)
|
||||
{
|
||||
switch (acc) {
|
||||
case '_':
|
||||
case 'b':
|
||||
return -1;
|
||||
case '^':
|
||||
case '#':
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Given a semitone value 0 - 11, convert to note + accidental.
|
||||
* This function always returns sharp as the accidental
|
||||
*/
|
||||
void note_for_semitone (int semitones, noteletter_t * note, char *accidental)
|
||||
{
|
||||
char note_for_semi[12] = "CCDDEFFGGAAB";
|
||||
char acc_for_semi[12] = " # # # # # ";
|
||||
|
||||
if (semitones < 0) {
|
||||
semitones = -(-semitones) % 12 + 12;
|
||||
}
|
||||
if (semitones > 11) {
|
||||
semitones = semitones % 12;
|
||||
}
|
||||
*note = note_index (note_for_semi[semitones]);
|
||||
*accidental = acc_for_semi[semitones];
|
||||
}
|
||||
|
||||
/* look for any octave shift specified after a clef name */
|
||||
static int get_clef_octave_offset (char *clef_ending)
|
||||
{
|
||||
if (strncmp (clef_ending, "+8", 2) == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (strncmp (clef_ending, "+15", 2) == 0) {
|
||||
return 2;
|
||||
}
|
||||
if (strncmp (clef_ending, "-8", 2) == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (strncmp (clef_ending, "-15", 2) == 0) {
|
||||
return -2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init_new_clef (cleftype_t * new_clef)
|
||||
{
|
||||
new_clef->basic_clef = basic_clef_undefined;
|
||||
new_clef->staveline = 0;
|
||||
new_clef->octave_offset = 0;
|
||||
new_clef->named = 0;
|
||||
}
|
||||
|
||||
/* copy contents of cleftype_t structure */
|
||||
void copy_clef (cleftype_t * target_clef, cleftype_t * source_clef)
|
||||
{
|
||||
target_clef->basic_clef = source_clef->basic_clef;
|
||||
target_clef->staveline = source_clef->staveline;
|
||||
target_clef->octave_offset = source_clef->octave_offset;
|
||||
target_clef->named = source_clef->named;
|
||||
}
|
||||
|
||||
/* use lookup table to get details of clef from it's name string */
|
||||
//int get_standard_clef (char *name, basic_cleftype_t * basic_clef,
|
||||
// int *staveline, int *octave_offset)
|
||||
int get_standard_clef (char *name, cleftype_t * new_clef)
|
||||
{
|
||||
int i;
|
||||
int len;
|
||||
|
||||
for (i = 0; i < NUM_CLEFS; i++) {
|
||||
const clef_item_t *table_row = &clef_conversion_table[i];
|
||||
|
||||
len = strlen (table_row->name);
|
||||
if (strncmp (name, table_row->name, len) == 0) {
|
||||
new_clef->basic_clef = table_row->basic_clef;
|
||||
new_clef->staveline = table_row->staveline;
|
||||
new_clef->named = 1;
|
||||
new_clef->octave_offset = get_clef_octave_offset (name + len);
|
||||
return 1; /* lookup succeeded */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* look for a clef using C, F or G and a number 1 - 5 or
|
||||
* one of the specials (none, perc, auto)
|
||||
*/
|
||||
//int get_extended_clef_details (char *name, basic_cleftype_t * basic_clef,
|
||||
// int *staveline, int *octave_offset)
|
||||
int get_extended_clef_details (char *name, cleftype_t * new_clef)
|
||||
{
|
||||
int i;
|
||||
int len;
|
||||
int num;
|
||||
int items_read;
|
||||
|
||||
for (i = 0; i < NUM_ODD_CLEFS; i++) {
|
||||
const clef_item_t *table_row = &odd_clef_conversion_table[i];
|
||||
|
||||
len = strlen (table_row->name);
|
||||
if (strncmp (name, table_row->name, len) == 0) {
|
||||
new_clef->basic_clef = table_row->basic_clef;
|
||||
new_clef->staveline = table_row->staveline;
|
||||
new_clef->octave_offset = table_row->octave_offset;
|
||||
new_clef->named = 1;
|
||||
new_clef->octave_offset = get_clef_octave_offset (name + len);
|
||||
return 1; /* lookup succeeded */
|
||||
}
|
||||
}
|
||||
new_clef->octave_offset = 0;
|
||||
/* try [C/F/G]{1-5] format */
|
||||
switch (name[0]) {
|
||||
case 'C':
|
||||
new_clef->basic_clef = basic_clef_alto;
|
||||
break;
|
||||
case 'F':
|
||||
new_clef->basic_clef = basic_clef_bass;
|
||||
break;
|
||||
case 'G':
|
||||
new_clef->basic_clef = basic_clef_treble;
|
||||
break;
|
||||
default:
|
||||
return 0; /* not recognized */
|
||||
}
|
||||
items_read = sscanf (&name[1], "%d", &num);
|
||||
if ((items_read == 1) && (num >= 1) && (num <= 5)) {
|
||||
/* we have a valid clef specification */
|
||||
new_clef->staveline = num;
|
||||
new_clef->named = 0;
|
||||
new_clef->octave_offset = get_clef_octave_offset (name + 2);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void append_octave_offset(char * name, int octave_offset)
|
||||
{
|
||||
switch (octave_offset) {
|
||||
case -2:
|
||||
strcat(name, "-15");
|
||||
break;
|
||||
case -1:
|
||||
strcat(name, "-8");
|
||||
break;
|
||||
case 1:
|
||||
strcat(name, "+8");
|
||||
break;
|
||||
case 2:
|
||||
strcat(name, "+15");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* given clef basic type and staveline, we try to work out it's name */
|
||||
int get_clef_name (cleftype_t * new_clef, char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (new_clef->named) {
|
||||
for (i = 0; i < NUM_CLEFS; i++) {
|
||||
if ((clef_conversion_table[i].basic_clef == new_clef->basic_clef) &&
|
||||
(clef_conversion_table[i].staveline == new_clef->staveline)) {
|
||||
strcpy (name, clef_conversion_table[i].name);
|
||||
append_octave_offset(name, new_clef->octave_offset);
|
||||
return 1; /* lookup succeeded */
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (new_clef->basic_clef) {
|
||||
default:
|
||||
case basic_clef_undefined:
|
||||
case basic_clef_none :
|
||||
return 0;
|
||||
case basic_clef_auto :
|
||||
strcpy(name, "auto");
|
||||
return 1;
|
||||
case basic_clef_perc :
|
||||
strcpy(name, "perc");
|
||||
return 1;
|
||||
case basic_clef_treble:
|
||||
name[0] = 'G';
|
||||
break;
|
||||
case basic_clef_bass:
|
||||
name[0] = 'F';
|
||||
break;
|
||||
case basic_clef_alto:
|
||||
name[0] = 'C';
|
||||
break;
|
||||
}
|
||||
name[1] = '0' + new_clef->staveline;
|
||||
name[2] = '\0';
|
||||
append_octave_offset(name, new_clef->octave_offset);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user