初始化内容
This commit is contained in:
Vendored
+431
@@ -0,0 +1,431 @@
|
||||
//
|
||||
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
||||
// Creation Date: Sun Feb 7 11:37:52 PST 2021
|
||||
// Last Modified: Sun Feb 7 14:12:31 PST 2021
|
||||
// Filename: tools/deltatimes.cpp
|
||||
// URL: https://github.com/craigsapp/midifile/blob/master/tools/deltatimes.cpp
|
||||
// Syntax: C++11
|
||||
// vim: ts=3
|
||||
//
|
||||
// Description: Generate list of delta times in a MIDI file, either as MIDI ticks,
|
||||
// quarter notes, or at real-time values in seconds or milliseconds
|
||||
// (why applying tempo meta messages to the tick values). Useful
|
||||
// for determining if the MIDI file contains a score or a performance.
|
||||
//
|
||||
// Options:
|
||||
// -q Output delta times as quarter note units instead of MIDI ticks.
|
||||
// -s Output real times in seconds.
|
||||
// -m Output real times in milliseconds.
|
||||
// -a Output MIDI tick, quarter note, second and millisecond timings.
|
||||
// -c Count timings by value (output a delta-time histogram).
|
||||
// -r Reverse: place the count after the timing values in the histogram.
|
||||
// -n # Normalize by the maximum count in the histogram scaled to number #.
|
||||
// -N # Normalize by the sum of the histogram counts scaled to number #.
|
||||
// -C Sort histogram by counts (largest to smallest).
|
||||
// --tpq Print ticks-per-quarter note from MIDI header(s) only.
|
||||
// --notes Calculate for MIDI notes only.
|
||||
// --on Calculate for MIDI notes-ons only.
|
||||
//
|
||||
|
||||
#include "MidiFile.h"
|
||||
#include "Options.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace smf;
|
||||
|
||||
#define TIME_TICKS 0
|
||||
#define TIME_SECONDS 1
|
||||
#define TIME_MILLISECONDS 2
|
||||
#define TIME_QUARTERS 3
|
||||
|
||||
|
||||
void processFile(MidiFile& midifile, map<string, int>& histogram, Options& options);
|
||||
void printHistogram(map<string, int>& histogram, Options& options);
|
||||
void extractDeltaTimesForAllEvents(MidiFile& midifile, map<string, int>& histogram, Options& options);
|
||||
void extractDeltaTimesForAllNotes(MidiFile& midifile, map<string, int>& histogram, Options& options);
|
||||
void extractDeltaTimesForAllNotesOns(MidiFile& midifile, map<string, int>& histogram, Options& options);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Options options;
|
||||
options.define("a|all=b", "output all types of time values for delta times");
|
||||
options.define("s|sec|second|seconds=b", "display times in seconds");
|
||||
options.define("m|ms|millisecond|milliseconds=b", "display times in milliseconds");
|
||||
options.define("q|quarter|quarters|quarter-notes|quarter-note=b", "display total time in quarter notes");
|
||||
options.define("c|count|counts=b", "display times in a histogram");
|
||||
options.define("n|normalize-by-max=d:100.0", "Normalize to max value in histogram");
|
||||
options.define("N|normalize-by-total=d:100.0", "Normalize to total count in histogram");
|
||||
options.define("r|reverse=b", "reverse histogram label/count order");
|
||||
options.define("C|sort-by-count=b", "sort histogram by counts rather than times");
|
||||
options.define("tpq=b", "print only the ticks-per-quarter note value from the MIDI header");
|
||||
options.define("notes|note=b", "delta times only between note on/offs");
|
||||
options.define("on|note-on|note-ons=b", "delta times only between note ons");
|
||||
options.process(argc, argv);
|
||||
|
||||
MidiFile midifile;
|
||||
map<string, int> histogram;
|
||||
|
||||
if (options.getArgCount() == 0) {
|
||||
midifile.read(cin);
|
||||
processFile(midifile, histogram, options);
|
||||
} else {
|
||||
int count = options.getArgCount();
|
||||
for (int i=0; i<count; i++) {
|
||||
string filename = options.getArg(i+1);
|
||||
midifile.read(filename);
|
||||
processFile(midifile, histogram, options);
|
||||
}
|
||||
}
|
||||
if (options.getBoolean("count") && !options.getBoolean("tpq")) {
|
||||
printHistogram(histogram, options);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////
|
||||
//
|
||||
// processFile -- Extract delta timings from the MIDI file, either
|
||||
// as raw ticks or as real times interpreted from tempo meta messages.
|
||||
//
|
||||
|
||||
void processFile(MidiFile& midifile, map<string, int>& histogram, Options& options) {
|
||||
if (options.getBoolean("tpq")) {
|
||||
int tpq = midifile.getTicksPerQuarterNote();
|
||||
cerr << tpq << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.getBoolean("notes")) {
|
||||
extractDeltaTimesForAllNotes(midifile, histogram, options);
|
||||
} else if (options.getBoolean("on")) {
|
||||
extractDeltaTimesForAllNotesOns(midifile, histogram, options);
|
||||
} else {
|
||||
extractDeltaTimesForAllEvents(midifile, histogram, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//
|
||||
// extractDeltaTimesForAllEvents -- calculate delta timings between all events.
|
||||
//
|
||||
|
||||
void extractDeltaTimesForAllEvents(MidiFile& midifile, map<string, int>& histogram,
|
||||
Options& options) {
|
||||
int tpq = midifile.getTicksPerQuarterNote();
|
||||
int timeFormat = TIME_TICKS;
|
||||
bool realtimeQ = false;
|
||||
bool allQ = options.getBoolean("all");
|
||||
if (allQ) {
|
||||
realtimeQ = true;
|
||||
}
|
||||
if (options.getBoolean("seconds")) {
|
||||
timeFormat = TIME_SECONDS;
|
||||
realtimeQ = true;
|
||||
} else if (options.getBoolean("milliseconds")) {
|
||||
timeFormat = TIME_MILLISECONDS;
|
||||
realtimeQ = true;
|
||||
} else if (options.getBoolean("quarters")) {
|
||||
timeFormat = TIME_QUARTERS;
|
||||
}
|
||||
bool histogramQ = options.getBoolean("count");
|
||||
|
||||
if (realtimeQ) {
|
||||
midifile.doTimeAnalysis();
|
||||
}
|
||||
|
||||
midifile.makeDeltaTicks();
|
||||
|
||||
for (int i=0; i<midifile.getTrackCount(); i++) {
|
||||
for (int j=0; j<midifile[i].getEventCount(); j++) {
|
||||
double value;
|
||||
double newtime;
|
||||
double oldtime;
|
||||
stringstream key;
|
||||
if (allQ) {
|
||||
key.str().clear();
|
||||
value = midifile[i][j].tick;
|
||||
key << value << "\t"; // MIDI ticks
|
||||
key << value/tpq << "\t"; // quarter notes
|
||||
newtime = midifile[i][j].seconds;
|
||||
if (j > 0) {
|
||||
oldtime = midifile[i][j-1].seconds;
|
||||
} else {
|
||||
oldtime = 0.0;
|
||||
}
|
||||
value = newtime - oldtime;
|
||||
key << value << "\t"; // seconds
|
||||
key << value * 1000.0; // milliseconds
|
||||
} else if (realtimeQ) {
|
||||
newtime = midifile[i][j].seconds;
|
||||
if (j == 0) {
|
||||
oldtime = 0;
|
||||
} else {
|
||||
oldtime = midifile[i][j-1].seconds;
|
||||
}
|
||||
value = newtime - oldtime;
|
||||
if (timeFormat == TIME_MILLISECONDS) {
|
||||
value *= 1000.0;
|
||||
}
|
||||
key.str().clear();
|
||||
key << value;
|
||||
} else {
|
||||
value = midifile[i][j].tick;
|
||||
if (timeFormat == TIME_QUARTERS) {
|
||||
value /= tpq;
|
||||
}
|
||||
key.str().clear();
|
||||
key << value;
|
||||
}
|
||||
if (histogramQ) {
|
||||
histogram[key.str()]++;
|
||||
} else {
|
||||
cout << key.str() << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//
|
||||
// extractDeltaTimesForAllNotes -- calculate delta timings between all note MIDI
|
||||
// messages (ignoring other MIDI messages).
|
||||
//
|
||||
|
||||
void extractDeltaTimesForAllNotes(MidiFile& midifile, map<string, int>& histogram,
|
||||
Options& options) {
|
||||
int tpq = midifile.getTicksPerQuarterNote();
|
||||
int timeFormat = TIME_TICKS;
|
||||
bool realtimeQ = false;
|
||||
bool allQ = options.getBoolean("all");
|
||||
int lastindex = -1;
|
||||
if (allQ) {
|
||||
realtimeQ = true;
|
||||
}
|
||||
if (options.getBoolean("seconds")) {
|
||||
timeFormat = TIME_SECONDS;
|
||||
realtimeQ = true;
|
||||
} else if (options.getBoolean("milliseconds")) {
|
||||
timeFormat = TIME_MILLISECONDS;
|
||||
realtimeQ = true;
|
||||
} else if (options.getBoolean("quarters")) {
|
||||
timeFormat = TIME_QUARTERS;
|
||||
}
|
||||
bool histogramQ = options.getBoolean("count");
|
||||
|
||||
if (realtimeQ) {
|
||||
midifile.doTimeAnalysis();
|
||||
}
|
||||
|
||||
for (int i=0; i<midifile.getTrackCount(); i++) {
|
||||
lastindex = -1;
|
||||
for (int j=0; j<midifile[i].getEventCount(); j++) {
|
||||
if (!midifile[i][j].isNote()) {
|
||||
continue;
|
||||
}
|
||||
if (lastindex < 0) {
|
||||
lastindex = j;
|
||||
continue;
|
||||
}
|
||||
double value;
|
||||
stringstream key;
|
||||
if (allQ) {
|
||||
key.str().clear();
|
||||
value = midifile[i][j].tick - midifile[i][lastindex].tick;
|
||||
key << value << "\t"; // MIDI ticks
|
||||
key << value/tpq << "\t"; // quarter notes
|
||||
value = midifile[i][j].seconds - midifile[i][lastindex].seconds;
|
||||
key << value << "\t"; // seconds
|
||||
key << value * 1000.0; // milliseconds
|
||||
} else if (realtimeQ) {
|
||||
value = midifile[i][j].seconds - midifile[i][lastindex].seconds;
|
||||
if (timeFormat == TIME_MILLISECONDS) {
|
||||
value *= 1000.0;
|
||||
}
|
||||
key.str().clear();
|
||||
key << value;
|
||||
} else {
|
||||
value = midifile[i][j].tick - midifile[i][lastindex].tick;
|
||||
if (timeFormat == TIME_QUARTERS) {
|
||||
value /= tpq;
|
||||
}
|
||||
key.str().clear();
|
||||
key << value;
|
||||
}
|
||||
if (histogramQ) {
|
||||
histogram[key.str()]++;
|
||||
} else {
|
||||
cout << key.str() << endl;
|
||||
}
|
||||
lastindex = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//
|
||||
// extractDeltaTimesForAllNoteOns -- calculate delta timings between all note-on MIDI
|
||||
// messages (ignoring other MIDI messages).
|
||||
//
|
||||
|
||||
void extractDeltaTimesForAllNotesOns(MidiFile& midifile, map<string, int>& histogram,
|
||||
Options& options) {
|
||||
int tpq = midifile.getTicksPerQuarterNote();
|
||||
int timeFormat = TIME_TICKS;
|
||||
bool realtimeQ = false;
|
||||
bool allQ = options.getBoolean("all");
|
||||
int lastindex = -1;
|
||||
if (allQ) {
|
||||
realtimeQ = true;
|
||||
}
|
||||
if (options.getBoolean("seconds")) {
|
||||
timeFormat = TIME_SECONDS;
|
||||
realtimeQ = true;
|
||||
} else if (options.getBoolean("milliseconds")) {
|
||||
timeFormat = TIME_MILLISECONDS;
|
||||
realtimeQ = true;
|
||||
} else if (options.getBoolean("quarters")) {
|
||||
timeFormat = TIME_QUARTERS;
|
||||
}
|
||||
bool histogramQ = options.getBoolean("count");
|
||||
|
||||
if (realtimeQ) {
|
||||
midifile.doTimeAnalysis();
|
||||
}
|
||||
|
||||
for (int i=0; i<midifile.getTrackCount(); i++) {
|
||||
lastindex = -1;
|
||||
for (int j=0; j<midifile[i].getEventCount(); j++) {
|
||||
if (!midifile[i][j].isNoteOn()) {
|
||||
continue;
|
||||
}
|
||||
if (lastindex < 0) {
|
||||
lastindex = j;
|
||||
continue;
|
||||
}
|
||||
double value;
|
||||
stringstream key;
|
||||
if (allQ) {
|
||||
key.str().clear();
|
||||
value = midifile[i][j].tick - midifile[i][lastindex].tick;
|
||||
key << value << "\t"; // MIDI ticks
|
||||
key << value/tpq << "\t"; // quarter notes
|
||||
value = midifile[i][j].seconds - midifile[i][lastindex].seconds;
|
||||
key << value << "\t"; // seconds
|
||||
key << value * 1000.0; // milliseconds
|
||||
} else if (realtimeQ) {
|
||||
value = midifile[i][j].seconds - midifile[i][lastindex].seconds;
|
||||
if (timeFormat == TIME_MILLISECONDS) {
|
||||
value *= 1000.0;
|
||||
}
|
||||
key.str().clear();
|
||||
key << value;
|
||||
} else {
|
||||
value = midifile[i][j].tick - midifile[i][lastindex].tick;
|
||||
if (timeFormat == TIME_QUARTERS) {
|
||||
value /= tpq;
|
||||
}
|
||||
key.str().clear();
|
||||
key << value;
|
||||
}
|
||||
if (histogramQ) {
|
||||
histogram[key.str()]++;
|
||||
} else {
|
||||
cout << key.str() << endl;
|
||||
}
|
||||
lastindex = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//
|
||||
// printHistogram --
|
||||
//
|
||||
|
||||
void printHistogram(map<string, int>& histogram, Options& options) {
|
||||
bool reverseQ = options.getBoolean("reverse");
|
||||
double scale = 1.0;
|
||||
if (options.getBoolean("normalize-by-max")) {
|
||||
int maximum = 0;
|
||||
for (auto it : histogram) {
|
||||
if (it.second > maximum) {
|
||||
maximum = it.second;
|
||||
}
|
||||
}
|
||||
if (maximum > 0) {
|
||||
scale = options.getDouble("normalize-by-max") / maximum;
|
||||
}
|
||||
} else if (options.getBoolean("normalize-by-total")) {
|
||||
int sum = 0;
|
||||
for (auto it : histogram) {
|
||||
sum += it.second;
|
||||
}
|
||||
if (sum > 0) {
|
||||
scale = options.getDouble("normalize-by-total") / sum;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.getBoolean("sort-by-count")) {
|
||||
// Sort by count, from lowest to highest:
|
||||
typedef function<bool(pair<int, string>, pair<int, string>)> Comparator;
|
||||
Comparator sortFunction = [](pair<int, string> a, pair<int, string> b) {
|
||||
if (a.first == b.first) {
|
||||
return stod(a.second) < stod(b.second);
|
||||
} else {
|
||||
return a.first > b.first;
|
||||
}
|
||||
};
|
||||
map<pair<int, string>, int, Comparator> sortedkeys(sortFunction);;
|
||||
for (auto it : histogram) {
|
||||
sortedkeys[make_pair(it.second, it.first)] = it.second;
|
||||
}
|
||||
for (auto it : sortedkeys) {
|
||||
string label = it.first.second;
|
||||
double count = it.second * scale;
|
||||
if (reverseQ) {
|
||||
cout << label << '\t' << count << endl;
|
||||
} else {
|
||||
cout << count << '\t' << label << endl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Sort by delta time, from lowest to highest:
|
||||
map<double, string, less<double>> sortedkeys;
|
||||
for (auto it : histogram) {
|
||||
sortedkeys[stod(it.first)] = it.first;
|
||||
}
|
||||
for (auto it : sortedkeys) {
|
||||
string label = it.second;
|
||||
double count = histogram[it.second] * scale;
|
||||
if (reverseQ) {
|
||||
cout << label << '\t' << count << endl;
|
||||
} else {
|
||||
cout << count << '\t' << label << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user