Compare commits

...

3 Commits

Author SHA1 Message Date
sshlien
0dd1e063ae 2023.12.17 2023-12-17 08:46:38 -05:00
sshlien
69c1f850cb 2023.11.26 2023-11-26 12:51:23 -05:00
sshlien
f470c694ac 2023.11.17 2023-11-17 13:51:31 -05:00
6 changed files with 136 additions and 15 deletions

View File

@@ -1,2 +1,2 @@
November 13 2023
December 17 2023

View File

@@ -15136,3 +15136,18 @@ handle certain midi files. The function stats_interpret_pulseCounter()
can detect midi files containing triplets and nonquantized notes.
midicopy: extended to handle midi files with up to 150 tracks.
December 17 2023
midicopy: The midi program number is not set properly for a few the
midi files where the channel program numbers are set in a separate
track. Midicopy processes and copies the tracks sequentially, and
by the time it sees the channel program numbers in the separate
track it is already too late. It would be necessary to rewrite
midicopy so that it stores the entire midi file in memory prior to
copying it to disk.
midistats: Introducing a new option -nseqfor n where is a channel
number. See drums.txt for a description.

View File

@@ -83,4 +83,25 @@ first two lines. In the next line the number of occurrences of
each value in the -ppat listing is given. The number in parentheses
splits the two 4-bit values with a period. Thus 33 = (2*16 + 1).
-nseqfor -n
Note sequence for channel n. This option produces a string for bytes
indicating the presence of a note in a time unit corresponding to
an eigth note. Thus each quarter note beat is represented by two
bytes. The pitch class is represented by the line number on the
staff, where 0 is C. Thus the notes on a scale are represented
by 7 numbers, and sharps and flats are ignored. The line number is
then converted to a bit position in the byte, so that the pitch
classes are represented by the numbers 1,2,4,8, and etc. A chord
of consisting of two note onsets would set two of the corresponding
bits. If we were to represent the full chromatic scale consisting
of 12 pitches, then we would require two-byte integers or
twice of much memory.
Though the pitch resolution is not sufficient to distinguish
major or minor chords, it should be sufficient to be identify some
repeating patterns.

View File

@@ -1,4 +1,4 @@
.TH MIDISTATS 1 "1 November 2023"
.TH MIDISTATS 1 "17 November 2023"
.SH NAME
\fBmidistats\fP \- program to summarize the statistical properties of a midi file
.SH SYNOPSIS

View File

@@ -6,7 +6,7 @@ abc2abc version 2.20 February 07 2023
yaps version 1.92 January 06 2023
abcmatch version 1.82 June 14 2022
midicopy version 1.39 November 08 2022
midistats version 0.80 November 13 2023
midistats version 0.82 December 17 2023
24th January 2002
Copyright James Allwright

View File

@@ -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;
}