Compare commits

...

2 Commits

Author SHA1 Message Date
Seymour Shlien
e49858809a 2023.03.15 2023-03-15 08:11:05 -04:00
Seymour Shlien
821240720d 2023.03.12 2023-03-12 13:45:41 -04:00
4 changed files with 206 additions and 13 deletions

View File

@@ -1,3 +1,2 @@
February 08 2023 March 15 2023

82
doc/drums.txt Executable file
View File

@@ -0,0 +1,82 @@
Advamced Percussion Analysis
in the Midistats Program
This is an addendum to the midistats.1 file.
The MIDI file devotes channel 9 to the percussion instruments
and over 60 percussion instruments are defined in the MIDI
standard. Though there is a lot of diversity in the percussion
track, for most MIDI files only the first 10 or so percussion
instruments are important in defining the character of the track. The
program Midiexplorer has various tools for exposing the percussion
channel which are described in the documentation. The goal
here is to find the essential characteristics of the percussion
track which distinguishes the MIDI files. This is attempted
in the program midistats. Here is a short description.
-corestats
Produces a line with 3 numbers separated by tabs. eg
384 8349 448
It returns the number of divisions per quarter note beat (ppqn),
the number of note onsets in the midi file, and the maximum
number of quarter note beats in midi file.
-pulseanalysis
Counts the number of note onsets as a function of its onset time
relative to a beat, grouping them into 12 intervals and returns
the result as a discrete probability density function. Generally,
the distribution consists of a couple of peaks corresponding
to quarter notes or eigth notes. If the distribution is flat,
it indicates that the times of the note occurrences have not been
quantized into beats and fractions. Here is a sample output.
0.3496,0.0000,0.0000,0.1602,0.0000,0.0002,0.2983,0.0000,0.0000,0.1914,0.0002,0.0001
-panal
Counts the number of note onsets for each percussion instrument. The first
number is the code (pitch) of the instrument, the second number is the
number of occurrences. eg.
35 337 37 16 38 432 39 208 40 231 42 1088 46 384 49 42 54 1104 57 5 70 1040 85 16
-ppatfor n
where n is the code number of the percussion instrument. Each beat
is represented by a 4 bit number where the position of the on-bit
indicates the time in the beat when the drum onset occurs. Thus
0 indicates that there was no note onset in that beat, 1 indicates
a note onset in the beginning of the beat, 4 indicates a note onset
in the middle of the beat, and etc. The function returns a string
of numbers ranging from 0 to 7 indicating the presence of note onsets
for the selected percussion instrument for the sequence of beats
in the midi file. Here is a truncated sample of the output.
0 0 0 0 0 0 0 0 1 0 0 4 1 0 0 4 1 0 0 4 1 0 0 4 1 0 0 4 1 0 0 4 1 4 4 0
1 0 0 0 1 0 5 0 1 0 5 0 1 0 5 0 1 0 5 0 1 0 5 0 1 0 5 0 1 0 5 0 1 0 0 0
1 0 5 0 1 0 5 0 1 etc.
One can see a repeating 4 beat pattern.
-ppat
midistats attempts to find two percussion instruments in the midi file
which come closest to acting as the bass drum and snare drum.
If it is unsuccessful, it returns a message of its failue. Otherwise,
encodes the position of these drum onsets in a 8 bit byte for each
quarter note beat in the midi file. The lower (right) 4 bits encode the
bass drum and the higher (left) 4 bits encode the snare drum in the
same manner as described above for -ppatfor.
0 0 0 0 0 0 0 0 0 0 33 145 33 145 33 145 33 145 33 145 33 145 33 145 33 145
33 145 33 145 33 145 33 145 33 145 33 145 33 145 33 145 33 145 33 145 33 145
33 145 33 145 33 145 33 145 33 145 33 and etc.
-ppathist
computes and displays the histogram of the values that would appear
when running the -ppat. eg.
bass 35 337
snare 38 432
1 (0.1) 64 32 (2.0) 8 33 (2.1) 136 144 (9.0) 8 145 (9.1) 136
The bass percussion code, the number of onsets, and the snare
percussion code and the number of onsets are given in the
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).

View File

@@ -80,6 +80,17 @@ all channels except the percussion channel.
collisions. Midistats counts the bar rhythm patterns using a hashing collisions. Midistats counts the bar rhythm patterns using a hashing
function. Presently collisions are ignored so occasionally two function. Presently collisions are ignored so occasionally two
distinct rhythm patterns are counted as one. distinct rhythm patterns are counted as one.
.SH Advance Percussion Analysis Tools
.PP
A number of experimental tools for analyzing the percussion channel
(track) were introduced into midistats and are accessible through
the runtime arguments. When these tools are used in a script which
runs through a collection of midi files, you can build a database
of percussion descriptors. Some more details are given in the
file drums.txt which comes with this documentation.
.SH AUTHOR .SH AUTHOR
Seymour Shlien <fy733@ncf.ca> Seymour Shlien <fy733@ncf.ca>

View File

@@ -18,7 +18,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 "0.62 March 08 2023 midistats" #define VERSION "0.67 March 14 2023 midistats"
#include <limits.h> #include <limits.h>
/* Microsoft Visual C++ Version 6.0 or higher */ /* Microsoft Visual C++ Version 6.0 or higher */
@@ -75,6 +75,8 @@ int debug;
int pulseanalysis; int pulseanalysis;
int percanalysis; int percanalysis;
int percpattern; int percpattern;
int percpatternfor;
int percpatternhist;
int corestats; 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 */
@@ -100,6 +102,8 @@ int stats = 0; /* flag - gather and print statistics */
int pulseanalysis = 0; int pulseanalysis = 0;
int percanalysis = 0; int percanalysis = 0;
int percpattern = 0; int percpattern = 0;
int percpatternfor = 0;
int percpatternhist = 0;
int corestats = 0; int corestats = 0;
@@ -111,6 +115,9 @@ int notechan[2048],notechanvol[2048]; /*for linking on and off midi
int last_tick[17]; /* for getting last pulse number in MIDI file */ int last_tick[17]; /* for getting last pulse number in MIDI file */
int last_on_tick[17]; /* for detecting chords [SS] 2019-08-02 */ int last_on_tick[17]; /* for detecting chords [SS] 2019-08-02 */
int histogram[256];
unsigned char drumpat[8000];
int percnum;
@@ -953,7 +960,6 @@ for (i = 0; i < lastEvent; i++) {
} }
void drumpattern (int perc) { void drumpattern (int perc) {
unsigned char drumpat[1000];
int i; int i;
int channel; int channel;
int pitch; int pitch;
@@ -963,7 +969,7 @@ int quarter;
int remainder; int remainder;
int part; int part;
quarter = division/4; quarter = division/4;
for (i = 0; i<1000; i++) drumpat[i] = 0; for (i = 0; i<8000; i++) drumpat[i] = 0;
for (i = 0; i <lastEvent; i++) { for (i = 0; i <lastEvent; i++) {
channel = midievents[i].channel; channel = midievents[i].channel;
if (channel != 9) continue; if (channel != 9) continue;
@@ -973,12 +979,77 @@ for (i = 0; i <lastEvent; i++) {
index = onset/division; index = onset/division;
remainder = onset % division; remainder = onset % division;
part = remainder/quarter; part = remainder/quarter;
if (index >= 8000) {printf("index too large in drumpattern\n");
break;
}
drumpat[index] = drumpat[index] |= 1 << part; drumpat[index] = drumpat[index] |= 1 << part;
} }
for (i=0;i<lastBeat;i++) printf("%d ",drumpat[i]); }
void dualDrumPattern (int perc1, int perc2) {
int i;
int channel;
int pitch;
int onset;
int index;
int quarter;
int remainder;
int part;
quarter = division/4;
for (i = 0; i<8000; i++) drumpat[i] = 0;
for (i = 0; i <lastEvent; i++) {
channel = midievents[i].channel;
if (channel != 9) continue;
pitch = midievents[i].pitch;
if (pitch != perc1 && pitch != perc2) continue;
onset = midievents[i].onsetTime;
index = onset/division;
if (index >= 8000) {printf("index too large in drumpattern\n");
break;
}
remainder = onset % division;
part = remainder/quarter;
if (pitch == perc1) drumpat[index] = drumpat[index] |= 1 << part;
else drumpat[index] = drumpat[index] |= 16 * (1 << part);
}
}
void output_perc_pattern (int i) {
int left,right;
left = i/16;
right = i % 16;
printf(" (%d.%d)",left,right);
}
void drumPatternHistogram () {
int i;
for (i=0;i<256;i++) {
histogram[i] = 0;
}
for (i=0;i<lastBeat;i++) {
histogram[drumpat[i]]++;
}
for (i=1;i<256;i++) {
if (histogram[i] > 0) {
printf(" %d",i);
output_perc_pattern(i);
printf(" %d ", histogram[i]);
}
}
printf("\n"); printf("\n");
} }
void output_drumpat () {
int i;
for (i=0;i<lastBeat;i++) printf("%d ",drumpat[i]);
/*for (i=0;i<lastBeat;i++) output_perc_pattern(drumpat[i]);*/
printf("\n");
}
void percsummary () { void percsummary () {
int i; int i;
int bassindex,snareindex; int bassindex,snareindex;
@@ -1002,8 +1073,9 @@ if (bassmax && snaremax) {
printf("snare %d %d\n",snareindex,snaremax); printf("snare %d %d\n",snareindex,snaremax);
} else { } else {
printf("missing bass or snare\n"); printf("missing bass or snare\n");
return;
} }
dualDrumPattern(bassindex,snareindex);
} }
@@ -1129,18 +1201,35 @@ int argc;
stats = 0; stats = 0;
} }
arg = getarg("-percanalysis",argc,argv); arg = getarg("-panal",argc,argv);
if (arg != -1) { if (arg != -1) {
percanalysis = 1; percanalysis = 1;
stats = 0; stats = 0;
} }
arg = getarg("-percpattern",argc,argv); arg = getarg("-ppat",argc,argv);
if (arg != -1) { if (arg != -1) {
percpattern = 1; percpattern = 1;
stats = 0; stats = 0;
} }
arg = getarg("-ppatfor",argc,argv);
if (arg != -1) {
percpatternfor = 1;
stats = 0;
if (arg != -1 && arg <argc) {
percnum = readnum(argv[arg]);
printf("percnum = %d\n",percnum);
}
}
arg = getarg("-ppathist",argc,argv);
if (arg != -1) {
percpatternhist = 1;
stats = 0;
}
arg = getarg("-o",argc,argv); arg = getarg("-o",argc,argv);
if ((arg != -1) && (arg < argc)) { if ((arg != -1) && (arg < argc)) {
outhandle = efopen(argv[arg],"w"); /* open output abc file */ outhandle = efopen(argv[arg],"w"); /* open output abc file */
@@ -1163,12 +1252,14 @@ int argc;
printf("midistats filename <options>\n"); printf("midistats filename <options>\n");
printf(" -corestats\n"); printf(" -corestats\n");
printf(" -pulseanalysis\n"); printf(" -pulseanalysis\n");
printf(" -percanalysis\n"); printf(" -panal\n");
printf(" -percpattern\n"); printf(" -ppat\n");
printf(" -ppatfor\n");
printf(" -ppathist\n");
printf(" -ver version number\n"); printf(" -ver version number\n");
printf(" -d <number> debug parameter\n"); printf(" -d <number> debug parameter\n");
printf(" The input filename is assumed to be any string not\n"); printf(" The input filename is assumed to be any string not\n");
printf(" beginning with a - (hyphen). It may be placed anywhere.\n"); printf(" beginning with a - (hyphen).\n");
exit(0); exit(0);
}; };
return arg; return arg;
@@ -1202,10 +1293,19 @@ if (percanalysis) {
} }
printf("\n"); printf("\n");
} }
if (percanalysis) drumpattern(40);
if (percpattern) { if (percpattern) {
drumanalysis(); drumanalysis();
percsummary(); percsummary();
output_drumpat();
}
if (percpatternfor) {
drumpattern(percnum);
output_drumpat();
}
if (percpatternhist) {
drumanalysis();
percsummary();
drumPatternHistogram();
} }
if (corestats) corestatsOutput(); if (corestats) corestatsOutput();
} }
@@ -1221,6 +1321,7 @@ int argc;
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 || percpattern) loadEvents(); if(pulseanalysis || corestats || percanalysis ||\
percpatternfor || percpattern || percpatternhist) loadEvents();
return 0; return 0;
} }