Compare commits

...

6 Commits

Author SHA1 Message Date
sshlien
4e0266179b 2023.10.25 2023-10-25 19:43:27 -04:00
sshlien
2bf0052eb8 2023.08.13 2023-09-13 17:36:44 -04:00
sshlien
1394cd96c5 2023.09.11 2023-09-11 09:41:32 -04:00
sshlien
8a2ec3a898 2023.09.06 2023-09-06 17:37:58 -04:00
sshlien
48c443fabd 2023.08.31 2023-08-31 08:44:52 -04:00
sshlien
3f405a1aba 2023.08.22 2023-08-22 09:21:59 -04:00
5 changed files with 115 additions and 37 deletions

View File

@@ -1,2 +1,2 @@
Juen 25 2023
October 25 2023

View File

@@ -15093,3 +15093,17 @@ be the acoutic piano (0) by default.
If more than one channel with the same program color, they should be
added together.
August 22 2023
Midistats improved the calculation of the number of rhythm patterns
in a channel (barChn[chan].rhythmPattern) by allowing for dithering
of note onset. Introduced dithermargin variable in function
stats_noteon(). In function, output_track_summary(), suppressed
notemeanpitch for percussion channel.
October 25 2023
Midistats returns track activity (note on/off) for every track.

View File

@@ -1,4 +1,4 @@
.TH MIDISTATS 1 "9 December 2022"
.TH MIDISTATS 1 "25 October 2023"
.SH NAME
\fBmidistats\fP \- program to summarize the statistical properties of a midi file
.SH SYNOPSIS
@@ -36,7 +36,14 @@ the sum of the MIDI pitches for all the notes,
the sum of the note durations in MIDI pulse units,
the number of control parameter messages,
the number of pressure messages.
and the number of distinct rhythm patterns for each channel
the number of distinct rhythm patterns for each channel
the number of pulses the channel was inactive
the minimum pitch value
the maximum pitch value
the minimum note length in pulses
the maximum note length in pulses
the number of gaps in the channel
the entropy of the pitch class histogram for that channel
.PP
After processing all the individual tracks, the following information
applies to the entire midi file.
@@ -70,9 +77,9 @@ that occur in the midi file.
pitchact is a similar histogram but is weighted by the length of
the notes.
.PP
quietTime is used to compute the track/channel spread in midiexplorer.
It is computed by summing up all the midi pulses which occur
in gaps greater than 8 beats.
chnact returns the amount of note activity in each channel.
.PP
trkact returns the number of notes in each track.
.PP
totalrhythmpatterns is the total number of bar rhythm patterns for
all channels except the percussion channel.

View File

@@ -6,20 +6,18 @@ abc2abc version 2.20 February 07 2023
yaps version 1.92 January 06 2023
abcmatch version 1.82 June 14 2022
midicopy version 1.38 May 06 2022
midistats version 0.59 February 08 2023
midistats version 0.77 October 25 2023
24th January 2002
Copyright James Allwright
J.R.Allwright@westminster.ac.uk
jamesallwright@yahoo.co.uk
University of Westminster,
London, UK
October 2021
Seymour Shlien
Ottawa, Canada
August 2023
Copyright Seymour Shlien
fy733@ncf.ca
Ottawa, Canada
This is free software. You may copy and re-distribute it under the terms of
the GNU General Public License version 2 or later, which is available from

View File

@@ -18,7 +18,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define VERSION "0.70 June 25 2023 midistats"
#define VERSION "0.77 October 25 2023 midistats"
#include <limits.h>
/* Microsoft Visual C++ Version 6.0 or higher */
@@ -52,7 +52,8 @@ void stats_finish();
float histogram_entropy (int *histogram, int size);
void stats_noteoff(int chan,int pitch,int vol);
void stats_eot ();
#define max(a,b) (( a > b ? a : b))
#define min(a,b) (( a < b ? a : b))
/* Global variables and structures */
@@ -62,6 +63,7 @@ static FILE *F;
static FILE *outhandle; /* for producing the abc file */
int tracknum=0; /* track number */
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 */
@@ -114,8 +116,10 @@ int trackcount = 0;
int notechan[2048],notechanvol[2048]; /*for linking on and off midi
channel commands */
int last_tick[17]; /* for getting last pulse number in MIDI file */
int lastTick[2048]; /* for getting last pulse number for chan (0-15) and pitch (0-127) in MIDI file */
int last_on_tick[17]; /* for detecting chords [SS] 2019-08-02 */
int channel_active[17]; /* for dealing with chords [SS] 2023-08-30 */
int channel_used_in_track[17]; /* for dealing with quietTime [SS] 2023-09-06 */
int histogram[256];
unsigned char drumpat[8000];
@@ -131,7 +135,11 @@ struct trkstat {
int notecount[17];
int chordcount[17];
int notemeanpitch[17];
int notepitchmin[17];
int notepitchmax[17];
int notelength[17];
int notelengthmin[17];
int notelengthmax[17];
int pitchbend[17];
int pressure[17];
int cntlparam[17];
@@ -141,6 +149,8 @@ struct trkstat {
int lastNoteOff[17];
int quietTime[17];
int rhythmpatterns[17];
int numberOfGaps[17];
float pitchEntropy[17];
} trkdata;
/* The trkstat references the individual channels in the midi file.
@@ -162,8 +172,10 @@ int pitchhistogram[12]; /* pitch distribution for non drum notes */
int channel2prog[17]; /* maps channel to program */
int channel2nnotes[17]; /*maps channel to note count */
int chnactivity[17]; /* [SS] 2018-02-02 */
int trkactivity[40]; /* [SS] 2023-10-25 */
int progactivity[128]; /* [SS] 2018-02-02 */
int pitchclass_activity[12]; /* [SS] 2018-02-02 */
int chanpitchhistogram[204]; /* [SS] 2023-09-13 */
/* [SS] 2017-11-01 */
@@ -407,6 +419,7 @@ void stats_header (int format, int ntrks, int ldivision)
quietLimit = ldivision*8;
divisionsPerBar = division*beatsPerBar;
unitDivision = divisionsPerBar/24;
lasttrack = ntrks; /* [SS] 2023-10-25 */
printf("ntrks %d\n",ntrks);
printf("ppqn %d\n",ldivision);
chordthreshold = ldivision/16; /* [SS] 2018-01-21 */
@@ -419,6 +432,7 @@ void stats_header (int format, int ntrks, int ldivision)
trkdata.cntlparam[i] = 0; /* [SS] 2022-03-04 */
trkdata.pressure[i] = 0; /* [SS] 2022-03-04 */
trkdata.quietTime[i] = 0; /* [SS] 2022-08-22 */
trkdata.numberOfGaps[i] = 0; /* [SS] 2023-09-07 */
progcolor[i] = 0;
channel2prog[i] = 0; /* [SS] 2023-06-25-8/
channel2nnotes[i] = 0;
@@ -428,6 +442,7 @@ void stats_header (int format, int ntrks, int ldivision)
for (i=0;i<12;i++) pitchhistogram[i] = 0; /* [SS] 2017-11-01 */
for (i=0;i<12;i++) pitchclass_activity[i] = 0; /* [SS] 2018-02-02 */
for (i=0;i<128;i++) progactivity[i] = 0; /* [SS] 2018-02-02 */
for (i=0;i<40;i++) trkactivity[i]=0; /* [SS] 2023-10-25 */
}
void determine_progcolor ()
@@ -513,17 +528,12 @@ else
for (i=0;i<12;i++) printf("%5.2f ",(double) pitchclass_activity[i]);
printf("\nchnact "); /* [SS] 2018-02-08 */
if (npulses > 0)
for (i=1;i<17;i++) printf("%5.2f ",chnactivity[i]/(double) trkdata.npulses[0]);
for (i=1;i<17;i++) printf("%5.3f ",chnactivity[i]/(double) trkdata.npulses[0]);
else
for (i=0;i<17;i++) printf("%5.2f ",(double) chnactivity[i]);
printf("\nquietTime ");
for (i=1;i<17;i++) {
delta = trkdata.npulses[0] - trkdata.quietTime[i];
if (trkdata.quietTime[i] < quietLimit) delta = 0;
delta = delta / (double) trkdata.npulses[0];
/* printf (" %5.3f ", delta); */
printf (" %d ", trkdata.quietTime[i]);
}
for (i=0;i<17;i++) printf("%5.3f ",(double) chnactivity[i]);
printf("\ntrkact ");
lasttrack++;
for (i=0;i<lasttrack;i++) printf("% 5d",trkactivity[i]);
printf("\npitchentropy %f\n",histogram_entropy(pitchclass_activity,12));
printf("totalrhythmpatterns =%d\n",nrpatterns);
@@ -541,8 +551,10 @@ float histogram_entropy (int *histogram, int size)
float e,p;
total = 0;
entropy = 0.0;
//printf("\nhistogram_entropy of:");
for (i=0;i<size;i++) {
total += histogram[i];
//printf(" %d",histogram[i]);
}
for (i=0;i<size;i++) {
if (histogram[i] < 1) continue;
@@ -550,6 +562,7 @@ float histogram_entropy (int *histogram, int size)
e = p*log(p);
entropy = entropy + e;
}
//printf("\n");
return -entropy/log(2.0);
}
@@ -584,10 +597,18 @@ for (i=1;i<17;i++) {
printf("trkinfo ");
printf("%d %d ",i,trkdata.program[i]); /* channel number and program*/
printf("%d %d ",trkdata.notecount[i],trkdata.chordcount[i]);
printf("%d %d ",trkdata.notemeanpitch[i], trkdata.notelength[i]);
/* [SS] 2023-08-22 */
if (i != 10) printf("%d %d ",trkdata.notemeanpitch[i], trkdata.notelength[i]);
else
printf("-1 0 ");
printf("%d %d ",trkdata.cntlparam[i],trkdata.pressure[i]); /* [SS] 2022-03-04 */
printf("%d %d",trkdata.quietTime[i],trkdata.rhythmpatterns[i]);
trkdata.quietTime[i] = 0;
printf("%d %d ",trkdata.quietTime[i],trkdata.rhythmpatterns[i]);
if (i != 10) {printf("%d %d %d %d %d",trkdata.notepitchmin[i], trkdata.notepitchmax[i] ,trkdata.notelengthmin[i], trkdata.notelengthmax[i], trkdata.numberOfGaps[i]);
printf(" %f",trkdata.pitchEntropy[i]);
} else
printf("-1 0");
trkdata.quietTime[i] = 0; /* in case channel i is used in another track */
trkdata.numberOfGaps[i] = 0;
printf("\n");
channel2nnotes[i] += trkdata.notecount[i] + trkdata.chordcount[i];
@@ -604,19 +625,35 @@ void stats_trackstart()
for (i=0;i<17;i++) {
trkdata.notecount[i] = 0;
trkdata.notemeanpitch[i] = 0;
trkdata.notepitchmin[i] = 128;
trkdata.notepitchmax[i] = 0;
trkdata.notelength[i] = 0;
trkdata.notelengthmin[i] = 10000;
trkdata.notelengthmax[i] = 0;
trkdata.chordcount[i] = 0;
trkdata.cntlparam[i] = 0;
last_tick[i] = -1;
last_on_tick[i] = -1;
channel_active[i] = 0;
}
printf("trk %d \n",tracknum);
for (i=0;i<2048;i++) lastTick[i] = -1;
for (i=0;i<17;i++) channel_used_in_track[i] = 0; /* [SS] 2023-09-06 */
for (i=0;i<204;i++) chanpitchhistogram[i] = 0; /* [SS] 2023-09-13 */
}
void stats_trackend()
{
trkdata.npulses[tracknum] = Mf_currtime;
int chan;
int i;
float entropy;
if (trkdata.npulses[0] < Mf_currtime) trkdata.npulses[0] = Mf_currtime;
for (chan = 1; chan < 17; chan++) /* [SS] 2023-09-06 */
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);
}
output_track_summary();
}
@@ -628,7 +665,12 @@ int chan, pitch, vol;
int delta;
int barnum;
int unit;
int dithermargin; /* [SS] 2023-08-22 */
int cpitch; /* [SS] 2023-09-13 */
cpitch = pitch % 12;
channel_used_in_track[chan+1]++; /* [SS] 2023-09-06 */
dithermargin = unitDivision/2 - 1;
if (vol == 0) {
/* treat as noteoff */
stats_noteoff(chan,pitch,vol);
@@ -636,17 +678,20 @@ int chan, pitch, vol;
return;
}
trkdata.notemeanpitch[chan+1] += pitch;
trkdata.notepitchmax[chan+1] = max(trkdata.notepitchmax[chan+1],pitch);
trkdata.notepitchmin[chan+1] = min(trkdata.notepitchmin[chan+1],pitch);
if (trkdata.lastNoteOff[chan+1] >= 0) {
delta = Mf_currtime - trkdata.lastNoteOff[chan+1];
trkdata.lastNoteOff[chan+1] = -1; /* in case of chord */
if (delta > quietLimit) {
trkdata.quietTime[chan+1] += delta;
trkdata.numberOfGaps[chan+1]++;
trkdata.lastNoteOff[chan+1] = -1; /* in case of chord */
}
}
if (abs(Mf_currtime - last_on_tick[chan+1]) < chordthreshold) trkdata.chordcount[chan+1]++;
else trkdata.notecount[chan+1]++; /* [SS] 2019-08-02 */
last_tick[chan+1] = Mf_currtime;
lastTick[chan*128 + pitch] = Mf_currtime;
last_on_tick[chan+1] = Mf_currtime; /* [SS] 2019-08-02 */
/* last_on_tick not updated by stats_noteoff */
@@ -661,9 +706,10 @@ int chan, pitch, vol;
barChn[chan].rhythmPattern = 0;
barChn[chan].activeBarNumber = barnum;
}
unit = (Mf_currtime % divisionsPerBar)/unitDivision;
unit = ((Mf_currtime+dithermargin) % divisionsPerBar)/unitDivision;
//printf("unit = %d pattern = %d \n",unit,barChn[chan].rhythmPattern);
barChn[chan].rhythmPattern = barChn[chan].rhythmPattern |= (1UL << unit);
chanpitchhistogram[chan*12+cpitch]++; /* [SS] 2023-09-13 */
}
@@ -674,6 +720,8 @@ int chan, pitch, vol;
else drumhistogram[pitch]++;
}
else pitchhistogram[pitch % 12]++; /* [SS] 2017-11-01 */
channel_active[chan+1]++;
}
@@ -686,17 +734,28 @@ void stats_noteoff(int chan,int pitch,int vol)
int length;
int program;
/* ignore if there was no noteon */
if (last_tick[chan+1] == -1) return;
length = Mf_currtime - last_tick[chan+1];
if (lastTick[chan*128+pitch] == -1) return;
length = Mf_currtime - lastTick[chan*128+pitch];
trkdata.notelength[chan+1] += length;
trkdata.notelengthmax[chan+1] = max(trkdata.notelengthmax[chan+1],length);
trkdata.notelengthmin[chan+1] = min(trkdata.notelengthmin[chan+1],length);
//if (length < 3) printf("chan = %d lasttick = %d currtime = %ld\n",chan,lastTick[chan*128+pitch],Mf_currtime);
trkdata.lastNoteOff[chan+1] = Mf_currtime; /* [SS] 2022.08.22 */
chnactivity[chan+1] += length;
trkactivity[tracknum]++;
if (chan == 9) return; /* drum channel */
pitchclass_activity[pitch % 12] += length;
program = trkdata.program[chan+1];
progactivity[program] += length;
channel_active[chan+1]--;
/* [SS] 2018-04-18 */
if(Mf_currtime > last_tick[chan+1]) last_tick[chan+1] = Mf_currtime;
if(Mf_currtime > lastTick[chan*128+pitch] && channel_active[chan+1] == 0)
lastTick[chan*128+pitch] = Mf_currtime; /* [SS] 2023.08.30 handle chords */
if (length > 4800) {
lastTick[chan*128+pitch] = Mf_currtime; /* handle stuck note [SS] 2023.08.30 */
channel_active[chan+1] = 0;
}
}