|
|
|
|
@@ -18,7 +18,7 @@
|
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define VERSION "0.80 November 13 2023 midistats"
|
|
|
|
|
#define VERSION "0.82 December 17 2023 midistats"
|
|
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
/* Microsoft Visual C++ Version 6.0 or higher */
|
|
|
|
|
@@ -49,7 +49,7 @@ extern char* strchr();
|
|
|
|
|
#include "midifile.h"
|
|
|
|
|
void initfuncs();
|
|
|
|
|
void stats_finish();
|
|
|
|
|
float histogram_entropy (int *histogram, int size);
|
|
|
|
|
float histogram_perplexity (int *histogram, int size);
|
|
|
|
|
void stats_noteoff(int chan,int pitch,int vol);
|
|
|
|
|
void stats_eot ();
|
|
|
|
|
#define max(a,b) (( a > b ? a : b))
|
|
|
|
|
@@ -67,6 +67,7 @@ int lasttrack = 0; /* lasttrack */
|
|
|
|
|
int division; /* pulses per quarter note defined in MIDI header */
|
|
|
|
|
int quietLimit; /* minimum number of pulses with no activity */
|
|
|
|
|
long tempo = 500000; /* the default tempo is 120 quarter notes/minute */
|
|
|
|
|
int bpm = 120; /*default tempo */
|
|
|
|
|
long laston = 0; /* length of MIDI track in pulses or ticks */
|
|
|
|
|
int key[12];
|
|
|
|
|
int sharps;
|
|
|
|
|
@@ -80,6 +81,7 @@ int percpattern;
|
|
|
|
|
int percpatternfor;
|
|
|
|
|
int percpatternhist;
|
|
|
|
|
int pitchclassanalysis;
|
|
|
|
|
int nseqfor;
|
|
|
|
|
int corestats;
|
|
|
|
|
int chordthreshold; /* number of maximum number of pulses separating note */
|
|
|
|
|
int beatsPerBar = 4; /* 4/4 time */
|
|
|
|
|
@@ -109,6 +111,7 @@ int percpattern = 0;
|
|
|
|
|
int percpatternfor = 0;
|
|
|
|
|
int percpatternhist = 0;
|
|
|
|
|
int pitchclassanalysis = 0;
|
|
|
|
|
int nseqfor = 0;
|
|
|
|
|
int corestats = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -124,7 +127,9 @@ int channel_used_in_track[17]; /* for dealing with quietTime [SS] 2023-09-06 */
|
|
|
|
|
|
|
|
|
|
int histogram[256];
|
|
|
|
|
unsigned char drumpat[8000];
|
|
|
|
|
unsigned char pseq[8000];
|
|
|
|
|
int percnum;
|
|
|
|
|
int nseqchn;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -475,15 +480,17 @@ int i;
|
|
|
|
|
/* [SS] 2023-10-30 */
|
|
|
|
|
void stats_interpret_pulseCounter () {
|
|
|
|
|
int i,j;
|
|
|
|
|
int maxcount,ncounts;
|
|
|
|
|
int maxcount,ncounts,npeaks,npositives,peaklimit;
|
|
|
|
|
int maxloc;
|
|
|
|
|
float threshold,peak;
|
|
|
|
|
int decimate;
|
|
|
|
|
float tripletsCriterion8,tripletsCriterion4;
|
|
|
|
|
int resolution = 12;
|
|
|
|
|
int nzeros;
|
|
|
|
|
threshold = 10.0/(float) division;
|
|
|
|
|
maxcount = 0;
|
|
|
|
|
ncounts = 0;
|
|
|
|
|
npeaks = 0;
|
|
|
|
|
for (i=0;i<division;i++) {
|
|
|
|
|
ncounts = ncounts + pulseCounter[i];
|
|
|
|
|
if (pulseCounter[i] > maxcount) {
|
|
|
|
|
@@ -491,6 +498,10 @@ for (i=0;i<division;i++) {
|
|
|
|
|
maxcount = pulseCounter[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
peaklimit = (int) (ncounts * 0.020);
|
|
|
|
|
for (i=0;i<division;i++) {
|
|
|
|
|
if (pulseCounter[i] > peaklimit) npeaks++;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < resolution; i++) pulseDistribution[i] = 0;
|
|
|
|
|
decimate = division/resolution;
|
|
|
|
|
for (i = 0; i < division; i++) {
|
|
|
|
|
@@ -498,14 +509,28 @@ for (i = 0; i < division; i++) {
|
|
|
|
|
pulseDistribution[j] += pulseCounter[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* count zeros */
|
|
|
|
|
nzeros = 0;
|
|
|
|
|
for (i=0;i<resolution;i++) if((float) pulseDistribution[i]/(float) ncounts < 0.015 ) nzeros++;
|
|
|
|
|
npositives = resolution - nzeros;
|
|
|
|
|
if (nzeros > 3 && (float) pulseDistribution[resolution-1]/(float) ncounts < 0.1) {printf("clean_quantization\n");
|
|
|
|
|
} else if ((float) pulseDistribution[resolution-1]/(float) ncounts > 0.09 ||
|
|
|
|
|
npeaks > npositives) {printf("dithered_quantization\n");
|
|
|
|
|
} else {
|
|
|
|
|
peak = (float) maxcount/ (float) ncounts;
|
|
|
|
|
/*printf("maxcount = %d ncounts = %d peak = %f threshold = %f\n",maxcount,ncounts,peak,threshold); */
|
|
|
|
|
if (peak < threshold) printf("unquantized\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tripletsCriterion8 = (float) pulseDistribution[8]/ (float) ncounts;
|
|
|
|
|
tripletsCriterion4 = (float) pulseDistribution[4]/ (float) ncounts;
|
|
|
|
|
/*printf("tripletsCriterion = %f\n",tripletsCriterion);*/
|
|
|
|
|
if (tripletsCriterion8 > 0.10 || tripletsCriterion4 > 0.10) printf("triplets\n");
|
|
|
|
|
if (pulseDistribution[0]/(float) ncounts > 0.95) printf("qnotes");
|
|
|
|
|
/*
|
|
|
|
|
printf("pulseDistribution:");
|
|
|
|
|
for (i=0;i<resolution;i++) printf("%6.3f",(float) pulseDistribution[i]/(float) ncounts);
|
|
|
|
|
printf("\n");
|
|
|
|
|
printf("nzeros = %d npeaks = %d \n",nzeros,npeaks);
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stats_finish()
|
|
|
|
|
@@ -574,7 +599,7 @@ else
|
|
|
|
|
printf("\ntrkact ");
|
|
|
|
|
lasttrack++;
|
|
|
|
|
for (i=0;i<lasttrack;i++) printf("% 5d",trkactivity[i]);
|
|
|
|
|
printf("\npitchentropy %f\n",histogram_entropy(pitchclass_activity,12));
|
|
|
|
|
printf("\npitchperplexity %f\n",histogram_perplexity(pitchclass_activity,12));
|
|
|
|
|
printf("totalrhythmpatterns =%d\n",nrpatterns);
|
|
|
|
|
printf("collisions = %d\n",ncollisions);
|
|
|
|
|
if (hasLyrics) printf("Lyrics\n");
|
|
|
|
|
@@ -584,8 +609,9 @@ printf("\n");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float histogram_entropy (int *histogram, int size)
|
|
|
|
|
float histogram_perplexity (int *histogram, int size)
|
|
|
|
|
{
|
|
|
|
|
/* The perplexity is 2 to the power of the entropy */
|
|
|
|
|
int i;
|
|
|
|
|
int total;
|
|
|
|
|
float entropy;
|
|
|
|
|
@@ -604,7 +630,7 @@ float histogram_entropy (int *histogram, int size)
|
|
|
|
|
entropy = entropy + e;
|
|
|
|
|
}
|
|
|
|
|
//printf("\n");
|
|
|
|
|
return -entropy/log(2.0);
|
|
|
|
|
return pow(2.0,-entropy/log(2.0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -693,7 +719,7 @@ void stats_trackend()
|
|
|
|
|
if (channel_used_in_track[chan] > 0) trkdata.quietTime[chan] += (trkdata.npulses[0] - trkdata.lastNoteOff[chan]);
|
|
|
|
|
for (chan=0;chan<16;chan++) { /* 2023-09-13 */
|
|
|
|
|
if (chan == 9 || channel_used_in_track[chan+1] == 0) continue;
|
|
|
|
|
trkdata.pitchEntropy[chan+1] = histogram_entropy(chanpitchhistogram +chan*12,11);
|
|
|
|
|
trkdata.pitchEntropy[chan+1] = histogram_perplexity(chanpitchhistogram +chan*12,11);
|
|
|
|
|
}
|
|
|
|
|
output_track_summary();
|
|
|
|
|
}
|
|
|
|
|
@@ -938,6 +964,12 @@ void record_noteoff(int chan,int pitch,int vol)
|
|
|
|
|
void record_trackend()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
void record_tempo(long ltempo)
|
|
|
|
|
{
|
|
|
|
|
tempo = ltempo;
|
|
|
|
|
if (bpm == 120) bpm = 60000000.0/tempo;
|
|
|
|
|
tempocount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int int_compare_events(const void *a, const void *b) {
|
|
|
|
|
struct eventstruc *ia = (struct eventstruc *)a;
|
|
|
|
|
@@ -1006,7 +1038,7 @@ void initfunc_for_loadNoteEvents()
|
|
|
|
|
Mf_eot = no_op0;
|
|
|
|
|
Mf_timesig = no_op4;
|
|
|
|
|
Mf_smpte = no_op5;
|
|
|
|
|
Mf_tempo = no_op1;
|
|
|
|
|
Mf_tempo = record_tempo;
|
|
|
|
|
Mf_keysig = no_op2;
|
|
|
|
|
Mf_seqspecific = no_op3;
|
|
|
|
|
Mf_text = no_op3;
|
|
|
|
|
@@ -1107,6 +1139,44 @@ for (i = 0; i <lastEvent; i++) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int pitch2noteseq[] = {
|
|
|
|
|
0, 0, 1, 1, 2, 3, 3, 4,
|
|
|
|
|
4, 5, 5, 6};
|
|
|
|
|
|
|
|
|
|
void noteseqmap(int chn) {
|
|
|
|
|
int i;
|
|
|
|
|
int half;
|
|
|
|
|
int channel;
|
|
|
|
|
int pitchclass;
|
|
|
|
|
int onset;
|
|
|
|
|
int index;
|
|
|
|
|
int remainder;
|
|
|
|
|
int noteNum;
|
|
|
|
|
int part;
|
|
|
|
|
printf("noteseqmap %d\n",chn);
|
|
|
|
|
half = division/2;
|
|
|
|
|
for (i = 0; i<8000; i++) pseq[i] = 0;
|
|
|
|
|
for (i = 0; i <lastEvent; i++) {
|
|
|
|
|
channel = midievents[i].channel;
|
|
|
|
|
if (channel != chn) continue;
|
|
|
|
|
pitchclass = midievents[i].pitch % 12;
|
|
|
|
|
noteNum = pitch2noteseq[pitchclass];
|
|
|
|
|
onset = midievents[i].onsetTime;
|
|
|
|
|
index = onset/half;
|
|
|
|
|
if (index >= 8000) {printf("index too large in drumpattern\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
pseq[index] = pseq[index] |= 1 << noteNum;
|
|
|
|
|
/*printf("pitchclass = %d noteNum =%d index = %d pseq[index] %d \n",pitchclass, noteNum, index, pseq[index]); */
|
|
|
|
|
}
|
|
|
|
|
printf("lastBeat = %d\n",lastBeat);
|
|
|
|
|
for (i=0;i<(lastBeat+1)*2;i++) {
|
|
|
|
|
printf("%d ",pseq[i]);
|
|
|
|
|
if (i >= 8000) break;
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dualDrumPattern (int perc1, int perc2) {
|
|
|
|
|
int i;
|
|
|
|
|
int channel;
|
|
|
|
|
@@ -1231,7 +1301,8 @@ int nchannels;
|
|
|
|
|
nchannels = 0;
|
|
|
|
|
for (i=1;i<17;i++)
|
|
|
|
|
if (channel_active[i] > 0) nchannels++;
|
|
|
|
|
printf("%d\t%d\t%d\t%d\t%d\n",lasttrack,nchannels, division,lastEvent,lastBeat);
|
|
|
|
|
printf("%d\t%d\t%d\t%d\t%d\t%d\n",lasttrack,nchannels, division,bpm,lastEvent,lastBeat);
|
|
|
|
|
/*printf("%d\n",tempocount);*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1374,6 +1445,16 @@ int argc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arg = getarg("-nseqfor",argc,argv);
|
|
|
|
|
if (arg != -1) {
|
|
|
|
|
nseqfor = 1;
|
|
|
|
|
stats = 0;
|
|
|
|
|
if (arg != -1 && arg <argc) {
|
|
|
|
|
nseqchn = readnum(argv[arg]);
|
|
|
|
|
printf("nseqch = %d\n",nseqchn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arg = getarg("-ppathist",argc,argv);
|
|
|
|
|
if (arg != -1) {
|
|
|
|
|
percpatternhist = 1;
|
|
|
|
|
@@ -1414,6 +1495,7 @@ int argc;
|
|
|
|
|
printf(" -ppatfor\n");
|
|
|
|
|
printf(" -ppathist\n");
|
|
|
|
|
printf(" -pitchclass\n");
|
|
|
|
|
printf(" -nseqfor\n");
|
|
|
|
|
printf(" -ver version number\n");
|
|
|
|
|
printf(" -d <number> debug parameter\n");
|
|
|
|
|
printf(" The input filename is assumed to be any string not\n");
|
|
|
|
|
@@ -1465,6 +1547,9 @@ if (percpatternhist) {
|
|
|
|
|
percsummary();
|
|
|
|
|
drumPatternHistogram();
|
|
|
|
|
}
|
|
|
|
|
if (nseqfor) {
|
|
|
|
|
noteseqmap(nseqchn);
|
|
|
|
|
}
|
|
|
|
|
if (corestats) corestatsOutput();
|
|
|
|
|
if (pitchclassanalysis) {
|
|
|
|
|
pitchClassAnalysis();
|
|
|
|
|
@@ -1485,6 +1570,6 @@ int argc;
|
|
|
|
|
if(stats == 1) midistats(argc,argv);
|
|
|
|
|
if(pulseanalysis || corestats || percanalysis ||\
|
|
|
|
|
percpatternfor || percpattern || percpatternhist ||\
|
|
|
|
|
pitchclassanalysis) loadEvents();
|
|
|
|
|
pitchclassanalysis || nseqfor) loadEvents();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|