mirror of
https://github.com/sshlien/abcmidi.git
synced 2026-04-15 14:23:41 +00:00
2026.02.13
This commit is contained in:
@@ -15667,4 +15667,7 @@ accidental_size prior to adding it to the pitchvalue.
|
|||||||
pitchvalue += microtoneshift*accidental_size; /* [SS] 2025.02.16 */
|
pitchvalue += microtoneshift*accidental_size; /* [SS] 2025.02.16 */
|
||||||
|
|
||||||
|
|
||||||
|
February 13 2026
|
||||||
|
|
||||||
|
midistats: added new option -keystability.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.TH MIDISTATS 1 "26 November 2025"
|
.TH MIDISTATS 1 "13 February 2026"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
\fBmidistats\fP \- program to summarize the statistical properties of a midi file
|
\fBmidistats\fP \- program to summarize the statistical properties of a midi file
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -323,6 +323,31 @@ splits the two 4-bit values with a period. Thus 33 = (2*16 + 1).
|
|||||||
.br
|
.br
|
||||||
Returns the pitch class distribution for the entire midi file.
|
Returns the pitch class distribution for the entire midi file.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
-keystability
|
||||||
|
.br
|
||||||
|
Seqments the midi file into 8 beat units (probably 2 bars), and
|
||||||
|
for each segment it uses Craig Sapp's algorithm to determine the
|
||||||
|
key signature based on the distribution of the pitch classes.
|
||||||
|
It returns the number of sharps (positive number) or the number
|
||||||
|
of flats (negative number) for that key signature. For example,
|
||||||
|
the key of G major or E minor would be represented by the value
|
||||||
|
1.
|
||||||
|
.br
|
||||||
|
The list of sharp/flats is returned in the line
|
||||||
|
.br
|
||||||
|
localkeysf 0 0 0 0 0 1 0 0 0 0 0 0 3 0 0 0 0 0 -1 -5 -5 -5 -5
|
||||||
|
.br
|
||||||
|
For example, in the above sequence (ABBA/Money, Money, Money.7.mid)
|
||||||
|
the key signature starts in A minor but switches to Bb minor in the
|
||||||
|
last 8 bars.
|
||||||
|
.br
|
||||||
|
Beware of enharmonic keys. For example,
|
||||||
|
B Major (5#) and Cb Major (7b) contain the same notes.
|
||||||
|
Similarly F# Major (6#) and Gb Major (6b), and
|
||||||
|
C# Major (7#) and Db Major (5b), and all the
|
||||||
|
minor key equivalents.
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
-nseqfor n
|
-nseqfor n
|
||||||
.br
|
.br
|
||||||
|
|||||||
176
midistats.c
176
midistats.c
@@ -17,7 +17,7 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define VERSION "1.01 November 26 2025 midistats"
|
#define VERSION "1.02 February 08 2026 midistats"
|
||||||
|
|
||||||
/* midistrats.c is a descendent of midi2abc.c which was becoming to
|
/* midistrats.c is a descendent of midi2abc.c which was becoming to
|
||||||
large. The object of the program is to extract statistical characterisitic
|
large. The object of the program is to extract statistical characterisitic
|
||||||
@@ -97,6 +97,7 @@ int maintrack;
|
|||||||
int format; /* MIDI file type */
|
int format; /* MIDI file type */
|
||||||
int debug;
|
int debug;
|
||||||
int noOutput;
|
int noOutput;
|
||||||
|
int keystabilityAnalysis;
|
||||||
int pulseanalysis;
|
int pulseanalysis;
|
||||||
int percanalysis;
|
int percanalysis;
|
||||||
int percpattern;
|
int percpattern;
|
||||||
@@ -108,6 +109,7 @@ int corestats;
|
|||||||
int chordthreshold; /* number of maximum number of pulses separating note */
|
int chordthreshold; /* number of maximum number of pulses separating note */
|
||||||
int beatsPerBar = 4; /* 4/4 time */
|
int beatsPerBar = 4; /* 4/4 time */
|
||||||
int divisionsPerBar;
|
int divisionsPerBar;
|
||||||
|
int divisionsPer4Bar;
|
||||||
int unitDivision;
|
int unitDivision;
|
||||||
int maximumPulse;
|
int maximumPulse;
|
||||||
int lastBeat;
|
int lastBeat;
|
||||||
@@ -127,6 +129,7 @@ int tempocount=0; /* number of tempo indications in MIDI file */
|
|||||||
|
|
||||||
int stats = 0; /* flag - gather and print statistics */
|
int stats = 0; /* flag - gather and print statistics */
|
||||||
|
|
||||||
|
int keystabilityAnalysis = 0;
|
||||||
int pulseanalysis = 0;
|
int pulseanalysis = 0;
|
||||||
int percanalysis = 0;
|
int percanalysis = 0;
|
||||||
int percpattern = 0;
|
int percpattern = 0;
|
||||||
@@ -225,6 +228,7 @@ int trkactivity[40]; /* [SS] 2023-10-25 */
|
|||||||
int progactivity[128]; /* [SS] 2018-02-02 */
|
int progactivity[128]; /* [SS] 2018-02-02 */
|
||||||
int pitchclass_activity[12]; /* [SS] 2018-02-02 */
|
int pitchclass_activity[12]; /* [SS] 2018-02-02 */
|
||||||
int chanpitchhistogram[204]; /* [SS] 2023-09-13 */
|
int chanpitchhistogram[204]; /* [SS] 2023-09-13 */
|
||||||
|
int local_pitchclass_activity[12]; /* [SS] 2026-02-08 */
|
||||||
|
|
||||||
|
|
||||||
/* [SS] 2017-11-01 */
|
/* [SS] 2017-11-01 */
|
||||||
@@ -476,6 +480,7 @@ void stats_header (int format, int ntrks, int ldivision)
|
|||||||
halfdivision = ldivision/2;
|
halfdivision = ldivision/2;
|
||||||
quietLimit = ldivision*8;
|
quietLimit = ldivision*8;
|
||||||
divisionsPerBar = division*beatsPerBar;
|
divisionsPerBar = division*beatsPerBar;
|
||||||
|
divisionsPer4Bar = divisionsPerBar*4;
|
||||||
unitDivision = divisionsPerBar/24;
|
unitDivision = divisionsPerBar/24;
|
||||||
lasttrack = ntrks; /* [SS] 2023-10-25 */
|
lasttrack = ntrks; /* [SS] 2023-10-25 */
|
||||||
if (noOutput == 0) printf("ntrks %d\n",ntrks);
|
if (noOutput == 0) printf("ntrks %d\n",ntrks);
|
||||||
@@ -1182,6 +1187,8 @@ void load_header (int format, int ntrks, int ldivision)
|
|||||||
int i;
|
int i;
|
||||||
division = ldivision;
|
division = ldivision;
|
||||||
halfdivision = ldivision/2;
|
halfdivision = ldivision/2;
|
||||||
|
divisionsPerBar = division*beatsPerBar;
|
||||||
|
divisionsPer4Bar = divisionsPerBar*4;
|
||||||
lasttrack = ntrks;
|
lasttrack = ntrks;
|
||||||
for (i=0;i<17;i++) channel_active[i] = 0; /* for counting number of channels*/
|
for (i=0;i<17;i++) channel_active[i] = 0; /* for counting number of channels*/
|
||||||
}
|
}
|
||||||
@@ -1489,6 +1496,35 @@ for (i = 0; i <lastEvent; i++) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int localKeyMatch ();
|
||||||
|
void reset_local_pitchclass_activity (int barIndex);
|
||||||
|
|
||||||
|
void localPitchClassAnalysis () {
|
||||||
|
int i;
|
||||||
|
int channel;
|
||||||
|
int pitch;
|
||||||
|
int bar4Index, lastbar4Index;
|
||||||
|
int sf;
|
||||||
|
printf("localkeysf ");
|
||||||
|
lastbar4Index = 0;
|
||||||
|
for (i=0;i<12;i++) local_pitchclass_activity[i] = 0;
|
||||||
|
for (i = 0; i < lastEvent; i++) {
|
||||||
|
channel = midievents[i].channel;
|
||||||
|
if (channel == 9) continue;
|
||||||
|
bar4Index = midievents[i].onsetTime/divisionsPer4Bar;
|
||||||
|
if (bar4Index != lastbar4Index) {
|
||||||
|
sf = localKeyMatch();
|
||||||
|
reset_local_pitchclass_activity(lastbar4Index);
|
||||||
|
printf(" %d",sf);
|
||||||
|
lastbar4Index = bar4Index;
|
||||||
|
}
|
||||||
|
pitch = midievents[i].pitch;
|
||||||
|
local_pitchclass_activity[pitch % 12]++;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
void pitchClassAnalysis () {
|
void pitchClassAnalysis () {
|
||||||
int i;
|
int i;
|
||||||
int channel;
|
int channel;
|
||||||
@@ -1502,6 +1538,17 @@ for (i = 0; i < lastEvent; i++) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void reset_local_pitchclass_activity (int barIndex) {
|
||||||
|
int i;
|
||||||
|
int sf;
|
||||||
|
float dist[12];
|
||||||
|
float maxdiscrepancy;
|
||||||
|
float dif;
|
||||||
|
//printf("%d\t",barIndex);
|
||||||
|
for (i=0;i<12;i++) local_pitchclass_activity[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void output_perc_pattern (int i) {
|
void output_perc_pattern (int i) {
|
||||||
int left,right;
|
int left,right;
|
||||||
left = i/16;
|
left = i/16;
|
||||||
@@ -1570,7 +1617,16 @@ static char *majmin[] = {"maj", "min"};
|
|||||||
|
|
||||||
/* number of sharps or flats for major keys in keylist */
|
/* number of sharps or flats for major keys in keylist */
|
||||||
static int maj2sf[] = {0, 7, 2, -3, 4, -1, 6, 1, -4, 3, -2, 5};
|
static int maj2sf[] = {0, 7, 2, -3, 4, -1, 6, 1, -4, 3, -2, 5};
|
||||||
static int min2sf[] = {-3, 4, -1, -6, -4, 3, -4 -2, -7, 0, -5, 2};
|
static int min2sf[] = {-3, 4, -1, -6, 1, -4, 3, -2, -7, 0, -5, 2};
|
||||||
|
|
||||||
|
void verify_arrays () {
|
||||||
|
int i;
|
||||||
|
for (i == 0; i <11; i++) {
|
||||||
|
printf("%d %s %d %d\n",i, keylist[i], maj2sf[i], min2sf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void keymatch () {
|
void keymatch () {
|
||||||
int i;
|
int i;
|
||||||
@@ -1598,14 +1654,6 @@ for (i=0;i<12;i++) {
|
|||||||
for (i=0;i<12;i++) {
|
for (i=0;i<12;i++) {
|
||||||
hist[i] = (float) pitchhistogram[i]/(float) total;
|
hist[i] = (float) pitchhistogram[i]/(float) total;
|
||||||
}
|
}
|
||||||
fnorm = 0.0;
|
|
||||||
for (i=0;i<12;i++) {
|
|
||||||
fnorm = hist[i]*hist[i] + fnorm;
|
|
||||||
}
|
|
||||||
fnorm = sqrt(fnorm);
|
|
||||||
for (i=0;i<12;i++) {
|
|
||||||
hist[i] = hist[i]/fnorm;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0;i<12;i++) {
|
for (i=0;i<12;i++) {
|
||||||
c2M += ssMj[i]*ssMj[i];
|
c2M += ssMj[i]*ssMj[i];
|
||||||
@@ -1651,6 +1699,100 @@ printf("\nrmin ");
|
|||||||
for (r=0;r<12;r++) printf("%7.3f",rmin[r]);
|
for (r=0;r<12;r++) printf("%7.3f",rmin[r]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********
|
||||||
|
int index2sf (int index, int bestMode) {
|
||||||
|
int sf;
|
||||||
|
if (bestMode == 0) sf = maj2sf[index];
|
||||||
|
else sf = min2sf[index];
|
||||||
|
printf("\t\t%s%s %d %d\t",keylist[index],majmin[bestMode],bestMode,sf);
|
||||||
|
return sf;
|
||||||
|
}
|
||||||
|
********/
|
||||||
|
|
||||||
|
int localKeyMatch () {
|
||||||
|
int i;
|
||||||
|
int r;
|
||||||
|
int k;
|
||||||
|
float c2M,c2m,h2,hM,hm;
|
||||||
|
float rmaj[12],rmin[12];
|
||||||
|
float best;
|
||||||
|
float fnorm;
|
||||||
|
float total;
|
||||||
|
float hist[12];
|
||||||
|
int bestIndex,bestMode;
|
||||||
|
int sf; /* number of flats or sharps (flats negative) */
|
||||||
|
|
||||||
|
for (i=0;i<12;i++) {
|
||||||
|
hist[i] = (float) local_pitchclass_activity[i];
|
||||||
|
}
|
||||||
|
fnorm = 0.0;
|
||||||
|
for (i=0;i<11;i++) fnorm += hist[i];
|
||||||
|
for (i=0;i<12;i++) hist[i] = hist[i]/fnorm;
|
||||||
|
|
||||||
|
c2M = 0.0;
|
||||||
|
c2m = 0.0;
|
||||||
|
h2 = 0.0;
|
||||||
|
best = 0.0;
|
||||||
|
bestIndex = 0;
|
||||||
|
bestMode = -1;
|
||||||
|
total =0;
|
||||||
|
fnorm = 0.0;
|
||||||
|
for (i=0;i<12;i++) {
|
||||||
|
fnorm = hist[i] + fnorm;
|
||||||
|
}
|
||||||
|
//fnorm = sqrt(fnorm);
|
||||||
|
for (i=0;i<12;i++) {
|
||||||
|
hist[i] = hist[i]/fnorm;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0;i<12;i++) {
|
||||||
|
c2M += ssMj[i]*ssMj[i];
|
||||||
|
c2m += ssMn[i]*ssMn[i];
|
||||||
|
h2 += hist[i]*hist[i];
|
||||||
|
}
|
||||||
|
if (h2 < 0.0001) {
|
||||||
|
printf("zero histogram\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (r=0;r<12;r++) {
|
||||||
|
hM = 0.0;
|
||||||
|
hm = 0.0;
|
||||||
|
for (i=0;i<12;i++) {
|
||||||
|
k = (i - r) % 12;
|
||||||
|
if (k < 0) k = k + 12;
|
||||||
|
hM += hist[i]*ssMj[k];
|
||||||
|
hm += hist[i]*ssMn[k];
|
||||||
|
}
|
||||||
|
rmaj[r] = hM/sqrt(h2*c2M);
|
||||||
|
rmin[r] = hm/sqrt(h2*c2m);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (r=0;r<12;r++) {
|
||||||
|
if(rmaj[r] > best) {
|
||||||
|
best = rmaj[r];
|
||||||
|
bestIndex = r;
|
||||||
|
bestMode = 0;
|
||||||
|
}
|
||||||
|
if(rmin[r] > best) {
|
||||||
|
best = rmin[r];
|
||||||
|
bestIndex = r;
|
||||||
|
bestMode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestMode == 0) sf = maj2sf[bestIndex];
|
||||||
|
else sf = min2sf[bestIndex];
|
||||||
|
|
||||||
|
//printf("\nkey %s%s %d %f",keylist[bestIndex],majmin[bestMode],sf,best);
|
||||||
|
//printf("hist:\t");
|
||||||
|
//for (r=0;r<12;r++) printf("%5.2f",hist[r]);
|
||||||
|
//printf("\nrmaj ");
|
||||||
|
//for (r=0;r<12;r++) printf("%7.3f",rmaj[r]);
|
||||||
|
//printf("\nrmin ");
|
||||||
|
//for (r=0;r<12;r++) printf("%7.3f",rmin[r]);
|
||||||
|
return sf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void percsummary () {
|
void percsummary () {
|
||||||
int i;
|
int i;
|
||||||
@@ -1815,6 +1957,13 @@ int argc;
|
|||||||
stats = 1;
|
stats = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arg = getarg("-keystability",argc,argv);
|
||||||
|
if (arg != -1) {
|
||||||
|
keystabilityAnalysis = 1;
|
||||||
|
stats = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
arg = getarg("-pulseanalysis",argc,argv);
|
arg = getarg("-pulseanalysis",argc,argv);
|
||||||
if (arg != -1) {
|
if (arg != -1) {
|
||||||
pulseanalysis = 1;
|
pulseanalysis = 1;
|
||||||
@@ -1909,6 +2058,7 @@ int argc;
|
|||||||
printf("midistats filename <options>\n");
|
printf("midistats filename <options>\n");
|
||||||
printf(" -corestats\n");
|
printf(" -corestats\n");
|
||||||
printf(" -CSV\n");
|
printf(" -CSV\n");
|
||||||
|
printf(" -keystability\n");
|
||||||
printf(" -pulseanalysis\n");
|
printf(" -pulseanalysis\n");
|
||||||
printf(" -panal\n");
|
printf(" -panal\n");
|
||||||
printf(" -ppat\n");
|
printf(" -ppat\n");
|
||||||
@@ -1983,6 +2133,9 @@ if (pitchclassanalysis) {
|
|||||||
pitchClassAnalysis();
|
pitchClassAnalysis();
|
||||||
outputPitchClassHistogram();
|
outputPitchClassHistogram();
|
||||||
}
|
}
|
||||||
|
if (keystabilityAnalysis) {
|
||||||
|
localPitchClassAnalysis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1994,10 +2147,11 @@ int argc;
|
|||||||
FILE *efopen();
|
FILE *efopen();
|
||||||
int arg;
|
int arg;
|
||||||
|
|
||||||
|
// verify_arrays();
|
||||||
arg = process_command_line_arguments(argc,argv);
|
arg = process_command_line_arguments(argc,argv);
|
||||||
if(stats == 1) midistats(argc,argv);
|
if(stats == 1) midistats(argc,argv);
|
||||||
if(pulseanalysis || corestats || percanalysis ||\
|
if(pulseanalysis || corestats || percanalysis ||\
|
||||||
percpatternfor || percpattern || percpatternhist ||\
|
percpatternfor || percpattern || percpatternhist ||\
|
||||||
pitchclassanalysis || nseqfor || nseqdistinct) loadEvents();
|
pitchclassanalysis || nseqfor || nseqdistinct || keystabilityAnalysis) loadEvents();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user