mirror of
https://github.com/sshlien/abcmidi.git
synced 2025-12-06 06:55:06 +00:00
4125 lines
106 KiB
C
4125 lines
106 KiB
C
/* midi2abc - program to convert MIDI files to abc notation.
|
|
* Copyright (C) 1998 James Allwright
|
|
* e-mail: J.R.Allwright@westminster.ac.uk
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
|
|
/* new midi2abc - converts MIDI file to abc format files
|
|
*
|
|
*
|
|
* re-written to use dynamic data structures
|
|
* James Allwright
|
|
* 5th June 1998
|
|
*
|
|
* added output file option -o
|
|
* added summary option -sum
|
|
* added option -u to enter xunit directly
|
|
* fixed computation of xunit using -b option
|
|
* added -obpl (one bar per line) option
|
|
* add check for carriage return embedded inside midi text line
|
|
* Seymour Shlien 04/March/00
|
|
* made to conform as much as possible to the official version.
|
|
* check for drum track added
|
|
* when midi program channel is command encountered, we ensure that
|
|
* we are using the correct channel number for the Voice by sending
|
|
* a %%MIDI channel message.
|
|
*
|
|
* Many more changes (see doc/CHANGES)
|
|
*
|
|
* Seymour Shlien 2005
|
|
*
|
|
* based on public domain 'midifilelib' package.
|
|
*/
|
|
|
|
#define VERSION "3.52 March 04 2022 midi2abc"
|
|
|
|
#include <limits.h>
|
|
/* Microsoft Visual C++ Version 6.0 or higher */
|
|
#ifdef _MSC_VER
|
|
#define snprintf _snprintf
|
|
#define ANSILIBS
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#ifdef PCCFIX
|
|
#define stdout 1
|
|
#endif
|
|
|
|
/* define USE_INDEX if your C libraries have index() instead of strchr() */
|
|
#ifdef USE_INDEX
|
|
#define strchr index
|
|
#endif
|
|
|
|
#ifdef ANSILIBS
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#else
|
|
extern char* malloc();
|
|
extern char* strchr();
|
|
#endif
|
|
#include "midifile.h"
|
|
#define BUFFSIZE 200
|
|
/* declare MIDDLE C */
|
|
#define MIDDLE 72
|
|
void initfuncs();
|
|
void setupkey(int);
|
|
void stats_finish();
|
|
int testtrack(int trackno, int barbeats, int anacrusis);
|
|
int open_note(int chan, int pitch, int vol);
|
|
int close_note(int chan, int pitch, int *initvol);
|
|
float histogram_entropy (int *histogram, int size);
|
|
void stats_noteoff(int chan,int pitch,int vol);
|
|
void reset_back_array (); /* [SS] 2019-05-08 */
|
|
|
|
|
|
/* Global variables and structures */
|
|
|
|
extern long Mf_toberead;
|
|
|
|
static FILE *F;
|
|
static FILE *outhandle; /* for producing the abc file */
|
|
|
|
int tracknum=0; /* track number */
|
|
int division; /* pulses per quarter note defined in MIDI header */
|
|
long tempo = 500000; /* the default tempo is 120 quarter notes/minute */
|
|
int unitlen; /* abc unit length usually defined in L: field */
|
|
int header_unitlen; /* first unitlen set */
|
|
int unitlen_set =0; /* once unitlen is set don't allow it to change */
|
|
int parts_per_unitlen = 2; /* specifies minimum quantization size */
|
|
long laston = 0; /* length of MIDI track in pulses or ticks */
|
|
char textbuff[BUFFSIZE]; /*buffer for handling text output to abc file*/
|
|
int trans[256], back[256]; /*translation tables for MIDI pitch to abc note*/
|
|
int barback[256]; /* to reinitialize back[] after each bar line */
|
|
char atog[256]; /* translation tables for MIDI pitch to abc note */
|
|
int symbol[256]; /*translation tables for MIDI pitch to abc note */
|
|
int key[12];
|
|
int sharps;
|
|
int trackno;
|
|
int maintrack;
|
|
int format; /* MIDI file type */
|
|
|
|
int karaoke, inkaraoke;
|
|
int midline;
|
|
int tempocount=0; /* number of tempo indications in MIDI file */
|
|
int gotkeysig=0; /*set to 1 if keysignature found in MIDI file */
|
|
|
|
/* global parameters that may be set by command line options */
|
|
int xunit; /* pulses per abc unit length */
|
|
int tsig_set; /* flag - time signature already set by user */
|
|
int ksig_set; /* flag - key signature already set by user */
|
|
int xunit_set;/* flat - xunit already set by user */
|
|
int extracta; /* flag - get anacrusis from strong beat */
|
|
int guessu; /* flag - estimate xunit from note durations */
|
|
int guessa; /* flag - get anacrusis by minimizing tied notes */
|
|
int guessk; /* flag - guess key signature */
|
|
int summary; /* flag - output summary info of MIDI file */
|
|
int keep_short; /*flag - preserve short notes */
|
|
int swallow_rests; /* flag - absorb short rests */
|
|
int midiprint; /* flag - run midigram instead of midi2abc */
|
|
int stats = 0; /* flag - gather and print statistics */
|
|
int usesplits; /* flag - split measure into parts if needed */
|
|
int restsize; /* smallest rest to absorb */
|
|
/* [SS] 2017-01-01 */
|
|
/*int no_triplets; flag - suppress triplets or broken rhythm */
|
|
int allow_triplets; /* flag to allow triplets */
|
|
int allow_broken; /* flag to allow broken rhythms > < */
|
|
int obpl = 0; /* flag to specify one bar per abc text line */
|
|
int nogr = 0; /* flag to put a space between every note */
|
|
int noly = 0; /* flag to allow lyrics [SS] 2019-07-12 */
|
|
int bars_per_line=4; /* number of bars per output line */
|
|
int bars_per_staff=4; /* number of bars per music staff */
|
|
int asig, bsig; /* time signature asig/bsig */
|
|
int header_asig =0; /* first time signature encountered */
|
|
int header_bsig =0; /* first time signature encountered */
|
|
int header_bb; /* first ticks/quarter note encountered */
|
|
int active_asig,active_bsig; /* last time signature declared */
|
|
int last_asig, last_ksig; /* last time signature printed */
|
|
int barsize; /* barsize in parts_per_unitlen units */
|
|
int chordthreshold; /* number of maximum number of pulses separating note */
|
|
int Qval; /* tempo - quarter notes per minute */
|
|
int verbosity=0; /* control amount of detail messages in abcfile*/
|
|
int debug=0; /* for debugging */
|
|
|
|
/* global arguments dependent on command line options or computed */
|
|
|
|
int anacrusis=0;
|
|
int bars;
|
|
int keysig;
|
|
int header_keysig= -50; /* header key signature */
|
|
int active_keysig = -50; /* last key signature declared */
|
|
int xchannel; /* channel number to be extracted. -1 means all */
|
|
int timeunits = 1; /*tells prtime to display time in beats [SS] 2018-10-25 */
|
|
double bend2cents = 40.96; /* [SS] 2022-02-12 */
|
|
|
|
|
|
/* structure for storing music notes */
|
|
struct anote {
|
|
int pitch; /* MIDI pitch */
|
|
int chan; /* MIDI channel */
|
|
int vel; /* MIDI velocity */
|
|
long time; /* MIDI onset time in pulses */
|
|
long dtnext; /* time increment to next note in pulses */
|
|
long tplay; /* note duration in pulses */
|
|
int xnum; /* number of xunits to next note */
|
|
int playnum; /* note duration in number of xunits */
|
|
int posnum; /* note position in xunits */
|
|
int splitnum; /* voice split number */
|
|
/* int denom; */
|
|
};
|
|
|
|
|
|
/* linked list of notes */
|
|
struct listx {
|
|
struct listx* next;
|
|
struct anote* note;
|
|
};
|
|
|
|
/* linked list of text items (strings) */
|
|
struct tlistx {
|
|
struct tlistx* next;
|
|
char* text;
|
|
long when; /* time in pulses to output */
|
|
int type; /* 0 - comments, other - field commands */
|
|
};
|
|
|
|
|
|
/* a MIDI track */
|
|
struct atrack {
|
|
struct listx* head; /* first note */
|
|
struct listx* tail; /* last note */
|
|
struct tlistx* texthead; /* first text string */
|
|
struct tlistx* texttail; /* last text string */
|
|
int notes; /* number of notes in track */
|
|
long tracklen;
|
|
long startwait;
|
|
int startunits;
|
|
int drumtrack;
|
|
/* [SS] 2019-05-29 for debugging */
|
|
int texts; /* number of text links in track */
|
|
};
|
|
|
|
/* can cope with up to 64 track MIDI files */
|
|
struct atrack track[64];
|
|
int trackcount = 0;
|
|
int maxbarcount = 0;
|
|
/* maxbarcount is used to return the numbers of bars created.*/
|
|
/* obpl is a flag for one bar per line. */
|
|
|
|
/* double linked list of notes */
|
|
/* used for temporary list of chords while abc is being generated */
|
|
struct dlistx {
|
|
struct dlistx* next;
|
|
struct dlistx* last;
|
|
struct anote* note;
|
|
};
|
|
|
|
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 last_on_tick[17]; /* for detecting chords [SS] 2019-08-02 */
|
|
|
|
char *title = NULL; /* for pasting title from argv[] */
|
|
char *origin = NULL; /* for adding O: info from argv[] */
|
|
int newline_flag = 0; /* [SS] 2019-06-14 signals new line was just issued */
|
|
|
|
|
|
void remove_carriage_returns();
|
|
int validnote();
|
|
void printpitch(struct anote*);
|
|
void printfract(int, int);
|
|
void txt_trackend();
|
|
void txt_noteoff(int,int,int);
|
|
/* [SS] 2019-05-29 new functions to handle type 0 midi files */
|
|
void txt_trackstart_type0();
|
|
void txt_noteon_type0(int,int,int);
|
|
void txt_program_type0(int,int);
|
|
|
|
/* The following variables are used by the -stats option
|
|
* which is used by a separate application called midiexplorer.tcl.
|
|
* The channel numbers go from 1 to 16 instead of 0 to 15
|
|
*/
|
|
struct trkstat {
|
|
int notecount[17];
|
|
int chordcount[17];
|
|
int notemeanpitch[17];
|
|
int notelength[17];
|
|
int pitchbend[17];
|
|
int pressure[17];
|
|
int cntlparam[17];
|
|
int program[17];
|
|
int tempo[17];
|
|
int npulses[17];
|
|
} trkdata;
|
|
|
|
/* The trkstat references the individual channels in the midi file.
|
|
* notecount is the number of notes or bass notes in the chord.
|
|
* chordcount is the number of notes not counting the bass notes.
|
|
* notemeanpitch is the average pitch for the channel.
|
|
* notelength is the average note length.
|
|
* pitchbend is the number of pitch bends for the channel.
|
|
* pressure is the number of control pressure commands.
|
|
* cntlparam is the number of control parameter commands.
|
|
* program is number of times there is a program command for the channel.
|
|
* tempo is the number of times there is a tempo command.
|
|
* npulses is the number of pulses.
|
|
*/
|
|
|
|
int progcolor[17]; /* used by stats_program */
|
|
int drumhistogram[82]; /* counts drum noteons */
|
|
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 progactivity[128]; /* [SS] 2018-02-02 */
|
|
int pitchclass_activity[12]; /* [SS] 2018-02-02 */
|
|
|
|
|
|
/* [SS] 2017-11-01 */
|
|
static int progmapper[] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 1, 1, 1, 1, 1, 1, 2,
|
|
3, 3, 3, 3, 3, 3, 3, 3,
|
|
2, 2, 4, 4, 4, 4, 4, 2,
|
|
5, 5, 5, 5, 5, 5, 5, 5,
|
|
6, 6, 6, 6, 6, 2, 7, 10,
|
|
7, 7, 7, 7, 8, 8, 8, 8,
|
|
9, 9, 9, 9, 9, 9, 9, 9,
|
|
11, 11, 11, 11, 11, 11, 11, 11,
|
|
12, 12, 12, 12, 12, 12, 12, 12,
|
|
13, 13, 13, 13, 13, 13, 13, 13,
|
|
14, 14, 14, 14, 14, 14, 14, 14,
|
|
15, 15, 15, 15, 15, 15, 15, 15,
|
|
2, 2, 2, 2, 2, 12, 6, 12,
|
|
1, 1, 10, 10, 10, 10, 10, 1,
|
|
16, 16, 16, 16, 16, 16, 16, 16
|
|
};
|
|
|
|
/* Stage 1. Parsing MIDI file */
|
|
|
|
/* Functions called during the reading pass of the MIDI file */
|
|
|
|
/* The following C routines are required by midifilelib. */
|
|
/* They specify the action to be taken when various items */
|
|
/* are encountered in the MIDI. The mfread function scans*/
|
|
/* the MIDI file and calls these functions when needed. */
|
|
|
|
|
|
int filegetc()
|
|
{
|
|
return(getc(F));
|
|
}
|
|
|
|
|
|
void fatal_error(s)
|
|
char* s;
|
|
/* fatal error encounterd - abort program */
|
|
{
|
|
fprintf(stderr, "%s\n", s);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
void event_error(s)
|
|
char *s;
|
|
/* problem encountered but OK to continue */
|
|
{
|
|
char msg[256];
|
|
|
|
sprintf(msg, "Error: Time=%ld Track=%d %s\n", Mf_currtime, trackno, s);
|
|
printf("%s",msg);
|
|
}
|
|
|
|
|
|
int* checkmalloc(bytes)
|
|
/* malloc with error checking */
|
|
int bytes;
|
|
{
|
|
int *p;
|
|
|
|
p = (int*) malloc(bytes);
|
|
if (p == NULL) {
|
|
fatal_error("Out of memory error - cannot malloc!");
|
|
};
|
|
return (p);
|
|
}
|
|
|
|
|
|
|
|
char* addstring(s)
|
|
/* create space for string and store it in memory */
|
|
char* s;
|
|
{
|
|
char* p;
|
|
int numbytes;
|
|
|
|
/* p = (char*) checkmalloc(strlen(s)+1); */
|
|
/* Integer overflow leading to heap buffer overflow in midi2abc
|
|
* Debian Bug report log #924947 [SS] 2019-08-11
|
|
* https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=924947
|
|
*/
|
|
numbytes = strlen(s)+2; /* [SS] 2019-08-11 */
|
|
if (numbytes > 1024) numbytes = 1024; /* [SS] 2019-08-11 */
|
|
p = (char*) checkmalloc(numbytes); /* [SS] 2019-04-13 2019-08-11*/
|
|
strncpy(p, s,numbytes); /* [SS] 2017-08-30 [SDG] 2020-06-03 */
|
|
p[numbytes-1] = '\0'; /* [JA] 2020-11-01 */
|
|
return(p);
|
|
}
|
|
|
|
void addtext(s, type)
|
|
/* add structure for text */
|
|
/* used when parsing MIDI file */
|
|
char* s;
|
|
int type;
|
|
{
|
|
struct tlistx* newx;
|
|
|
|
track[trackno].texts = track[trackno].texts + 1; /* [SS] 2019-05-29 */
|
|
newx = (struct tlistx*) checkmalloc(sizeof(struct tlistx));
|
|
newx->next = NULL;
|
|
newx->text = addstring(s);
|
|
newx->type = type;
|
|
newx->when = Mf_currtime;
|
|
if (track[trackno].texthead == NULL) {
|
|
track[trackno].texthead = newx;
|
|
track[trackno].texttail = newx;
|
|
}
|
|
else {
|
|
track[trackno].texttail->next = newx;
|
|
track[trackno].texttail = newx;
|
|
};
|
|
}
|
|
|
|
/* [SS] 2019-05-2019 forwards text to track[chn] */
|
|
void addtext_type0(s, type,chn)
|
|
/* add structure for text */
|
|
/* used when parsing MIDI file */
|
|
char* s;
|
|
int type;
|
|
int chn;
|
|
{
|
|
struct tlistx* newx;
|
|
|
|
track[chn].texts = track[chn].texts + 1;
|
|
newx = (struct tlistx*) checkmalloc(sizeof(struct tlistx));
|
|
newx->next = NULL;
|
|
newx->text = addstring(s);
|
|
newx->type = type;
|
|
newx->when = Mf_currtime;
|
|
if (track[chn].texthead == NULL) {
|
|
track[chn].texthead = newx;
|
|
track[chn].texttail = newx;
|
|
}
|
|
else {
|
|
track[chn].texttail->next = newx;
|
|
track[chn].texttail = newx;
|
|
};
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The MIDI file has separate commands for starting */
|
|
/* and stopping a note. In order to determine the duration of */
|
|
/* the note it is necessary to find the note_on command associated */
|
|
/* with the note off command. We rely on the note's pitch and channel*/
|
|
/* number to find the right note. While we are parsing the MIDI file */
|
|
/* we maintain a list of all the notes that are currently on */
|
|
/* head and tail of list of notes still playing. */
|
|
/* The following doubly linked list is used for this purpose */
|
|
|
|
struct dlistx* playinghead;
|
|
struct dlistx* playingtail;
|
|
|
|
|
|
void noteplaying(p)
|
|
/* This function adds a new note to the playinghead list. */
|
|
struct anote* p;
|
|
{
|
|
struct dlistx* newx;
|
|
|
|
newx = (struct dlistx*) checkmalloc(sizeof(struct dlistx));
|
|
newx->note = p;
|
|
newx->next = NULL;
|
|
newx->last = playingtail;
|
|
if (playinghead == NULL) {
|
|
playinghead = newx;
|
|
};
|
|
if (playingtail == NULL) {
|
|
playingtail = newx;
|
|
}
|
|
else {
|
|
playingtail->next = newx;
|
|
playingtail = newx;
|
|
};
|
|
}
|
|
|
|
|
|
void addnote(p, ch, v)
|
|
/* add structure for note */
|
|
/* used when parsing MIDI file */
|
|
int p, ch, v;
|
|
{
|
|
struct listx* newx;
|
|
struct anote* newnote;
|
|
|
|
track[trackno].notes = track[trackno].notes + 1;
|
|
newx = (struct listx*) checkmalloc(sizeof(struct listx));
|
|
newnote = (struct anote*) checkmalloc(sizeof(struct anote));
|
|
newx->next = NULL;
|
|
newx->note = newnote;
|
|
if (track[trackno].head == NULL) {
|
|
track[trackno].head = newx;
|
|
track[trackno].tail = newx;
|
|
}
|
|
else {
|
|
track[trackno].tail->next = newx;
|
|
track[trackno].tail = newx;
|
|
};
|
|
if (ch == 9) {
|
|
track[trackno].drumtrack = 1;
|
|
};
|
|
newnote->pitch = p;
|
|
newnote->chan = ch;
|
|
newnote->vel = v;
|
|
newnote->time = Mf_currtime;
|
|
laston = Mf_currtime;
|
|
newnote->tplay = Mf_currtime;
|
|
noteplaying(newnote);
|
|
}
|
|
|
|
/* [SS] 2019-05-29 forwards note to track[ch] */
|
|
void addnote_type0(p, ch, v)
|
|
/* add structure for note */
|
|
/* used when parsing MIDI file */
|
|
int p, ch, v;
|
|
{
|
|
struct listx* newx;
|
|
struct anote* newnote;
|
|
|
|
track[ch].notes = track[ch].notes + 1;
|
|
newx = (struct listx*) checkmalloc(sizeof(struct listx));
|
|
newnote = (struct anote*) checkmalloc(sizeof(struct anote));
|
|
newx->next = NULL;
|
|
newx->note = newnote;
|
|
if (track[ch].head == NULL) {
|
|
track[ch].head = newx;
|
|
track[ch].tail = newx;
|
|
}
|
|
else {
|
|
track[ch].tail->next = newx;
|
|
track[ch].tail = newx;
|
|
};
|
|
if (ch == 9) {
|
|
track[ch].drumtrack = 1;
|
|
};
|
|
newnote->pitch = p;
|
|
newnote->chan = ch;
|
|
newnote->vel = v;
|
|
newnote->time = Mf_currtime;
|
|
laston = Mf_currtime;
|
|
newnote->tplay = Mf_currtime;
|
|
noteplaying(newnote);
|
|
}
|
|
|
|
|
|
|
|
void notestop(p, ch)
|
|
/* MIDI note stops */
|
|
/* used when parsing MIDI file */
|
|
int p, ch;
|
|
{
|
|
struct dlistx* i;
|
|
int found;
|
|
char msg[80];
|
|
|
|
i = playinghead;
|
|
found = 0;
|
|
while ((found == 0) && (i != NULL)) {
|
|
if ((i->note->pitch == p)&&(i->note->chan==ch)) {
|
|
found = 1;
|
|
}
|
|
else {
|
|
i = i->next;
|
|
};
|
|
};
|
|
if (found == 0) {
|
|
sprintf(msg, "Note terminated when not on - pitch %d", p);
|
|
event_error(msg);
|
|
return;
|
|
};
|
|
/* fill in tplay field */
|
|
i->note->tplay = Mf_currtime - (i->note->tplay);
|
|
/* remove note from list */
|
|
if (i->last == NULL) {
|
|
playinghead = i->next;
|
|
}
|
|
else {
|
|
(i->last)->next = i->next;
|
|
};
|
|
if (i->next == NULL) {
|
|
playingtail = i->last;
|
|
}
|
|
else {
|
|
(i->next)->last = i->last;
|
|
};
|
|
free(i);
|
|
}
|
|
|
|
|
|
|
|
|
|
FILE *
|
|
efopen(name,mode)
|
|
char *name;
|
|
char *mode;
|
|
{
|
|
FILE *f;
|
|
|
|
if ( (f=fopen(name,mode)) == NULL ) {
|
|
char msg[256];
|
|
sprintf(msg,"Error - Cannot open file %s",name);
|
|
fatal_error(msg);
|
|
}
|
|
return(f);
|
|
}
|
|
|
|
void error(s)
|
|
char *s;
|
|
{
|
|
fprintf(stderr,"Error: %s\n",s);
|
|
}
|
|
|
|
|
|
/* [SS] 2017-11-19 */
|
|
void stats_error(s)
|
|
char *s;
|
|
{
|
|
fprintf(stderr,"Error: %s\n",s);
|
|
fprintf(stderr,"activetrack %d\n",tracknum);
|
|
stats_finish();
|
|
}
|
|
|
|
|
|
|
|
void txt_header(xformat,ntrks,ldivision)
|
|
int xformat, ntrks, ldivision;
|
|
{
|
|
division = ldivision;
|
|
format = xformat;
|
|
if (format != 0) {
|
|
/* fprintf(outhandle,"%% format %d file %d tracks\n", format, ntrks);*/
|
|
if(summary>0) printf("This midi file has %d tracks\n\n",ntrks);
|
|
}
|
|
else {
|
|
/* fprintf(outhandle,"%% type 0 midi file\n"); */
|
|
/* [SS] 2019-05-29 use these functions for type 0 midi files */
|
|
Mf_trackstart = txt_trackstart_type0;
|
|
Mf_trackend = txt_trackend;
|
|
Mf_noteon = txt_noteon_type0;
|
|
Mf_noteoff = txt_noteoff;
|
|
Mf_program = txt_program_type0;
|
|
if(summary>0) {
|
|
printf("This is a type 0 midi file.\n");
|
|
printf("All the channels are in one track.\n");
|
|
printf("You may need to process the channels separately\n\n");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void txt_trackstart()
|
|
{
|
|
laston = 0L;
|
|
track[trackno].notes = 0;
|
|
track[trackno].texts = 0; /* [SS] 2019-05-29 */
|
|
track[trackno].head = NULL;
|
|
track[trackno].tail = NULL;
|
|
track[trackno].texthead = NULL;
|
|
track[trackno].texttail = NULL;
|
|
track[trackno].tracklen = Mf_currtime;
|
|
track[trackno].drumtrack = 0;
|
|
}
|
|
|
|
|
|
/* [SS] 2019-05-29 new function. We need all 16 tracks
|
|
* to handle type 0 midi file. Each track links to one
|
|
* of the 16 midi channels */
|
|
void txt_trackstart_type0()
|
|
{
|
|
int i;
|
|
for (i=0;i<16;i++) {
|
|
track[i].notes = 0;
|
|
track[i].texts = 0; /* [SS] 2019-05-29 */
|
|
track[i].head = NULL;
|
|
track[i].tail = NULL;
|
|
track[i].texthead = NULL;
|
|
track[i].texttail = NULL;
|
|
track[i].tracklen = Mf_currtime;
|
|
track[i].drumtrack = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void txt_trackend()
|
|
{
|
|
/* check for unfinished notes */
|
|
if (playinghead != NULL) {
|
|
printf("Error in MIDI file - notes still on at end of track!\n");
|
|
};
|
|
track[trackno].tracklen = Mf_currtime - track[trackno].tracklen;
|
|
trackno = trackno + 1;
|
|
trackcount = trackcount + 1;
|
|
}
|
|
|
|
void txt_noteon(chan,pitch,vol)
|
|
int chan, pitch, vol;
|
|
{
|
|
if ((xchannel == -1) || (chan == xchannel)) {
|
|
if (vol != 0) {
|
|
addnote(pitch, chan, vol);
|
|
}
|
|
else {
|
|
notestop(pitch, chan);
|
|
};
|
|
};
|
|
}
|
|
|
|
/* [SS] 2019-05-29 new. Calls addnote_type0() function */
|
|
void txt_noteon_type0(chan,pitch,vol)
|
|
int chan, pitch, vol;
|
|
{
|
|
if ((xchannel == -1) || (chan == xchannel)) {
|
|
if (vol != 0) {
|
|
addnote_type0(pitch, chan, vol);
|
|
}
|
|
else {
|
|
notestop(pitch, chan);
|
|
};
|
|
};
|
|
}
|
|
|
|
void txt_noteoff(chan,pitch,vol)
|
|
int chan, pitch, vol;
|
|
{
|
|
if ((xchannel == -1) || (chan == xchannel)) {
|
|
notestop(pitch, chan);
|
|
};
|
|
}
|
|
|
|
void txt_pressure(chan,pitch,press)
|
|
int chan, pitch, press;
|
|
{
|
|
}
|
|
|
|
void stats_pressure(chan,press)
|
|
int chan, press;
|
|
{
|
|
trkdata.pressure[0]++;
|
|
}
|
|
|
|
void txt_parameter(chan,control,value)
|
|
int chan, control, value;
|
|
{
|
|
}
|
|
|
|
void txt_pitchbend(chan,lsb,msb)
|
|
int chan, msb, lsb;
|
|
{
|
|
}
|
|
|
|
void txt_program(chan,program)
|
|
int chan, program;
|
|
{
|
|
/* suppress the %%MIDI program for channel 10 */
|
|
if (chan == 9) return; /* [SS] 2020-02-17 */
|
|
sprintf(textbuff, "%%%%MIDI program %d", program);
|
|
addtext(textbuff,0);
|
|
/* abc2midi does not use the same channel number as specified in
|
|
the original midi file, so we should not specify that channel
|
|
number in the %%MIDI program. If we leave it out the program
|
|
will refer to the current channel assigned to this voice.
|
|
*/
|
|
}
|
|
|
|
/* [SS] 2019-05-29 new. Calls addtext_type0 function. */
|
|
void txt_program_type0(chan,program)
|
|
int chan, program;
|
|
{
|
|
if (chan == 9) return; /* [SS] 2020-02-17 */
|
|
sprintf(textbuff, "%%%%MIDI program %d", program);
|
|
addtext_type0(textbuff,0,chan);
|
|
/* abc2midi does not use the same channel number as specified in
|
|
the original midi file, so we should not specify that channel
|
|
number in the %%MIDI program. If we leave it out the program
|
|
will refer to the current channel assigned to this voice.
|
|
*/
|
|
}
|
|
|
|
void txt_sysex(leng,mess)
|
|
int leng;
|
|
char *mess;
|
|
{
|
|
}
|
|
|
|
void txt_metamisc(type,leng,mess)
|
|
int type, leng;
|
|
char *mess;
|
|
{
|
|
}
|
|
|
|
void txt_metaspecial(type,leng,mess)
|
|
int type, leng;
|
|
char *mess;
|
|
{
|
|
}
|
|
|
|
void txt_metatext(type,leng,mess)
|
|
int type, leng;
|
|
char *mess;
|
|
{
|
|
char *ttype[] = {
|
|
NULL,
|
|
"Text Event", /* type=0x01 */
|
|
"Copyright Notice", /* type=0x02 */
|
|
"Sequence/Track Name",
|
|
"Instrument Name", /* ... */
|
|
"Lyric",
|
|
"Marker",
|
|
"Cue Point", /* type=0x07 */
|
|
"Unrecognized"
|
|
};
|
|
int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1;
|
|
unsigned char c;
|
|
int n;
|
|
char *p = mess;
|
|
char *buff;
|
|
char buffer2[BUFFSIZE+4]; /* [SS] 2022-02-12 */
|
|
|
|
if ((type < 1)||(type > unrecognized))
|
|
type = unrecognized;
|
|
buff = textbuff;
|
|
for (n=0; n<leng; n++) {
|
|
c = *p++;
|
|
if (buff - textbuff < BUFFSIZE - 6) {
|
|
sprintf(buff,
|
|
(isprint(c)||isspace(c)) ? "%c" : "\\0x%02x" , c);
|
|
buff = buff + strlen(buff);
|
|
};
|
|
}
|
|
if (strncmp(textbuff, "@KMIDI KARAOKE FILE", 14) == 0) {
|
|
karaoke = 1;
|
|
}
|
|
else {
|
|
if ((karaoke == 1) && (*textbuff != '@')) {
|
|
addtext(textbuff,0);
|
|
}
|
|
else {
|
|
if (leng < BUFFSIZE - 3) {
|
|
snprintf(buffer2, sizeof(buffer2), " %s", textbuff); /*[SDG] 2020-06-03*/
|
|
addtext(buffer2,type); /* [SS] 2019-07-12 */
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|
|
void txt_metaseq(num)
|
|
int num;
|
|
{
|
|
sprintf(textbuff, "%%Meta event, sequence number = %d",num);
|
|
addtext(textbuff,0);
|
|
}
|
|
|
|
void txt_metaeot()
|
|
/* Meta event, end of track */
|
|
{
|
|
}
|
|
|
|
void txt_keysig(sf,mi)
|
|
char sf, mi;
|
|
{
|
|
int accidentals;
|
|
gotkeysig =1;
|
|
sprintf(textbuff,
|
|
"%% MIDI Key signature, sharp/flats=%d minor=%d",
|
|
(int) sf, (int) mi);
|
|
if(verbosity) addtext(textbuff,0);
|
|
sprintf(textbuff,"%d %d\n",sf,mi);
|
|
if (!ksig_set) {
|
|
addtext(textbuff,1);
|
|
keysig=sf;
|
|
}
|
|
if (header_keysig == -50) header_keysig = keysig;
|
|
if (summary <= 0) return;
|
|
/* There may be several key signature changes in the midi
|
|
file so that key signature in the mid file does not conform
|
|
with the abc file. Show all key signature changes.
|
|
*/
|
|
accidentals = (int) sf;
|
|
if (accidentals <0 )
|
|
{
|
|
accidentals = -accidentals;
|
|
printf("Key signature: %d flats", accidentals);
|
|
}
|
|
else
|
|
printf("Key signature : %d sharps", accidentals);
|
|
if (ksig_set) printf(" suppressed\n");
|
|
else printf("\n");
|
|
}
|
|
|
|
void txt_tempo(ltempo)
|
|
long ltempo;
|
|
{
|
|
if(tempocount>0) return; /* ignore other tempo indications */
|
|
tempo = ltempo;
|
|
tempocount++;
|
|
}
|
|
|
|
|
|
void setup_timesig(nn, denom, bb)
|
|
int nn,denom,bb;
|
|
{
|
|
asig = nn;
|
|
bsig = denom;
|
|
/* we must keep unitlen and xunit fixed for the entire tune */
|
|
if (unitlen_set == 0) {
|
|
unitlen_set = 1;
|
|
if ((asig*4)/bsig >= 3) {
|
|
unitlen =8;
|
|
}
|
|
else {
|
|
unitlen = 16;
|
|
};
|
|
}
|
|
/* set xunit for this unitlen */
|
|
if(!xunit_set) xunit = (division*bb*4)/(8*unitlen);
|
|
barsize = parts_per_unitlen*asig*unitlen/bsig;
|
|
/* printf("setup_timesig: unitlen=%d xunit=%d barsize=%d\n",unitlen,xunit,barsize); */
|
|
if (header_asig ==0) {header_asig = asig;
|
|
header_bsig = bsig;
|
|
header_unitlen = unitlen;
|
|
header_bb = bb;
|
|
}
|
|
}
|
|
|
|
|
|
void txt_timesig(nn,dd,cc,bb)
|
|
int nn, dd, cc, bb;
|
|
{
|
|
int denom = 1;
|
|
while ( dd-- > 0 )
|
|
denom *= 2;
|
|
sprintf(textbuff,
|
|
"%% Time signature=%d/%d MIDI-clocks/click=%d 32nd-notes/24-MIDI-clocks=%d",
|
|
nn,denom,cc,bb);
|
|
if (verbosity) addtext(textbuff,0);
|
|
sprintf(textbuff,"%d %d %d\n",nn,denom,bb);
|
|
if (!tsig_set) {
|
|
addtext(textbuff,2);
|
|
setup_timesig(nn, denom,bb);
|
|
}
|
|
if (summary>0) {
|
|
if(tsig_set) printf("Time signature = %d/%d suppressed\n",nn,denom);
|
|
else printf("Time signature = %d/%d\n",nn,denom);
|
|
}
|
|
}
|
|
|
|
|
|
void txt_smpte(hr,mn,se,fr,ff)
|
|
int hr, mn, se, fr, ff;
|
|
{
|
|
}
|
|
|
|
void txt_arbitrary(leng,mess)
|
|
char *mess;
|
|
int leng;
|
|
{
|
|
}
|
|
|
|
|
|
|
|
/* Dummy functions for handling MIDI messages.
|
|
* */
|
|
void no_op0() {}
|
|
void no_op1(int dummy1) {}
|
|
void no_op2(int dummy1, int dummy2) {}
|
|
void no_op3(int dummy1, int dummy2, int dummy3) { }
|
|
void no_op4(int dummy1, int dummy2, int dummy3, int dummy4) { }
|
|
void no_op5(int dummy1, int dummy2, int dummy3, int dummy4, int dummy5) { }
|
|
|
|
|
|
void print_txt_header(xformat,ntrks,ldivision)
|
|
int xformat, ntrks, ldivision;
|
|
{
|
|
division = ldivision;
|
|
format = xformat;
|
|
printf("Header %d %d %d\n",format,ntrks,ldivision); /*[SS] 2019-11-13 */
|
|
}
|
|
|
|
|
|
void print_txt_noteon(chan, pitch, vol)
|
|
int chan, pitch, vol;
|
|
{
|
|
int start_time;
|
|
int initvol;
|
|
if (vol > 0)
|
|
open_note(chan, pitch, vol);
|
|
else {
|
|
start_time = close_note(chan, pitch,&initvol);
|
|
if (start_time >= 0)
|
|
/* printf("%8.4f %8.4f %d %d %d %d\n",
|
|
(double) start_time/(double) division,
|
|
(double) Mf_currtime/(double) division,
|
|
trackno+1, chan +1, pitch,initvol);
|
|
*/
|
|
printf("%d %ld %d %d %d %d\n",
|
|
start_time, Mf_currtime, trackno+1, chan +1, pitch,initvol);
|
|
|
|
if(Mf_currtime > last_tick[chan+1]) last_tick[chan+1] = Mf_currtime;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void print_txt_noteoff(chan, pitch, vol)
|
|
int chan, pitch, vol;
|
|
{
|
|
int start_time,initvol;
|
|
|
|
start_time = close_note(chan, pitch, &initvol);
|
|
if (start_time >= 0)
|
|
/*
|
|
printf("%8.4f %8.4f %d %d %d %d\n",
|
|
(double) start_time/(double) division,
|
|
(double) Mf_currtime/(double) division,
|
|
trackno+1, chan+1, pitch,initvol);
|
|
*/
|
|
printf("%d %ld %d %d %d %d\n",
|
|
start_time, Mf_currtime, trackno+1, chan +1, pitch,initvol);
|
|
if(Mf_currtime > last_tick[chan+1]) last_tick[chan+1] = Mf_currtime;
|
|
}
|
|
|
|
|
|
|
|
/* In order to associate a channel note off message with its
|
|
* corresponding note on message, we maintain the information
|
|
* the notechan array. When a midi pitch (0-127) is switched
|
|
* on for a particular channel, we record the time that it
|
|
* was turned on in the notechan array. As there are 16 channels
|
|
* and 128 pitches, we initialize an array 128*16 = 2048 elements
|
|
* long.
|
|
**/
|
|
void init_notechan()
|
|
{
|
|
/* signal that there are no active notes */
|
|
int i;
|
|
for (i = 0; i < 2048; i++) notechan[i] = -1;
|
|
}
|
|
|
|
|
|
/* The next two functions update notechan when a channel note on
|
|
or note off is encountered. The second function close_note,
|
|
returns the time when the note was turned on.
|
|
*/
|
|
int open_note(int chan, int pitch, int vol)
|
|
{
|
|
notechan[128 * chan + pitch] = Mf_currtime;
|
|
notechanvol[128 * chan + pitch] = vol;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int close_note(int chan, int pitch, int *initvol)
|
|
{
|
|
int index, start_tick;
|
|
index = 128 * chan + pitch;
|
|
if (notechan[index] < 0)
|
|
return -1;
|
|
start_tick = notechan[index];
|
|
*initvol = notechanvol[index];
|
|
notechan[index] = -1;
|
|
return start_tick;
|
|
}
|
|
|
|
void print_txt_program(int chan,int program) {
|
|
/* [SS] 2019-11-06 */
|
|
printf("%ld Program %2d %d \n",Mf_currtime,chan+1, program);
|
|
/*printf("Program %2d %d \n",chan+1, program);*/
|
|
}
|
|
|
|
/* mftext mode */
|
|
int prtime(int units)
|
|
{
|
|
/* if(Mf_currtime >= pulses) ignore=0;
|
|
if (ignore) return 1;
|
|
linecount++;
|
|
if(linecount > maxlines) {fclose(F); exit(0);}
|
|
*/
|
|
if(units==1)
|
|
/*seconds*/
|
|
printf("%6.2f ",mf_ticks2sec(Mf_currtime,division,tempo));
|
|
else if (units==2)
|
|
/*beats*/
|
|
printf("%6.2f ",(float) Mf_currtime/(float) division);
|
|
else
|
|
/*pulses*/
|
|
printf("%6ld ",Mf_currtime);
|
|
return 0;
|
|
}
|
|
|
|
char * pitch2key(int note)
|
|
{
|
|
static char name[16]; /* [SDG] 2020-06-03 */
|
|
char* s = name;
|
|
switch(note % 12)
|
|
{
|
|
case 0: *s++ = 'c'; break;
|
|
case 1: *s++ = 'c'; *s++ = '#'; break;
|
|
case 2: *s++ = 'd'; break;
|
|
case 3: *s++ = 'd'; *s++ = '#'; break;
|
|
case 4: *s++ = 'e'; break;
|
|
case 5: *s++ = 'f'; break;
|
|
case 6: *s++ = 'f'; *s++ = '#'; break;
|
|
case 7: *s++ = 'g'; break;
|
|
case 8: *s++ = 'g'; *s++ = '#'; break;
|
|
case 9: *s++ = 'a'; break;
|
|
case 10: *s++ = 'a'; *s++ = '#'; break;
|
|
case 11: *s++ = 'b'; break;
|
|
}
|
|
sprintf(s, "%d", (note / 12)-1); /* octave (assuming Piano C4 is 60)*/
|
|
return name;
|
|
}
|
|
|
|
|
|
void pitch2drum(midipitch)
|
|
int midipitch;
|
|
{
|
|
static char *drumpatches[] = {
|
|
"Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare",
|
|
"Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi Hat",
|
|
"High Floor Tom", "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat",
|
|
"Low-Mid Tom", "Hi Mid Tom", "Crash Cymbal 1", "High Tom",
|
|
"Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine",
|
|
"Splash Cymbal", "Cowbell", "Crash Cymbal 2", "Vibraslap",
|
|
"Ride Cymbal 2", "Hi Bongo", "Low Bongo", "Mute Hi Conga",
|
|
"Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale",
|
|
"High Agogo", "Low Agogo", "Cabasa", "Maracas",
|
|
"Short Whistle", "Long Whistle", "Short Guiro", "Long Guiro",
|
|
"Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica",
|
|
"Open Cuica", "Mute Triangle", "Open Triangle" };
|
|
if (midipitch >= 35 && midipitch <= 81) {
|
|
printf(" (%s)",drumpatches[midipitch-35]);
|
|
}
|
|
}
|
|
|
|
|
|
void mftxt_header (int format, int ntrks, int ldivision)
|
|
{
|
|
division = ldivision;
|
|
printf("Header format=%d ntrks=%d division=%d\n",format,ntrks,division);
|
|
}
|
|
|
|
|
|
void stats_header (int format, int ntrks, int ldivision)
|
|
{
|
|
int i;
|
|
division = ldivision;
|
|
printf("ntrks %d\n",ntrks);
|
|
printf("ppqn %d\n",ldivision);
|
|
chordthreshold = ldivision/16; /* [SS] 2018-01-21 */
|
|
trkdata.tempo[0] = 0;
|
|
trkdata.pressure[0] = 0;
|
|
trkdata.program[0] = 0;
|
|
for (i=0;i<17;i++) {
|
|
trkdata.npulses[i] = 0;
|
|
trkdata.pitchbend[i] = 0;
|
|
trkdata.cntlparam[i] = 0; /* [SS] 2022-03-04 */
|
|
trkdata.pressure[i] = 0; /* [SS] 2022-03-04 */
|
|
progcolor[i] = 0;
|
|
channel2prog[i] = -1;
|
|
channel2nnotes[i] = 0;
|
|
chnactivity[i] = 0; /* [SS] 2018-02-02 */
|
|
}
|
|
for (i=0;i<82;i++) drumhistogram[i] = 0;
|
|
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 */
|
|
}
|
|
|
|
void determine_progcolor ()
|
|
{
|
|
int i;
|
|
for (i=0;i<17;i++) progcolor[i] =0;
|
|
for (i=0;i<128;i++) {
|
|
progcolor[progmapper[i]] += progactivity[i];
|
|
}
|
|
}
|
|
|
|
|
|
/* [SS] 2018-04-24 */
|
|
void output_progs_data () {
|
|
int i;
|
|
/* check that there is valid progactivity */
|
|
|
|
printf("progs ");
|
|
for (i=0;i<128;i++)
|
|
if(progactivity[i] > 0) printf(" %d",i);
|
|
printf("\nprogsact ");
|
|
for (i=0;i<128;i++)
|
|
if(progactivity[i] > 0) printf(" %d",progactivity[i]);
|
|
}
|
|
|
|
|
|
|
|
void stats_finish()
|
|
{
|
|
int i; /* [SDG] 2020-06-03 */
|
|
int npulses;
|
|
int nprogs;
|
|
|
|
npulses = trkdata.npulses[0];
|
|
printf("npulses %d\n",trkdata.npulses[0]);
|
|
printf("tempocmds %d\n",trkdata.tempo[0]);
|
|
printf("pitchbends %d\n",trkdata.pitchbend[0]);
|
|
for (i=1;i<17;i++) {
|
|
if (trkdata.pitchbend[i] > 0) {
|
|
printf("pitchbendin %d %d\n",i,trkdata.pitchbend[i]); }
|
|
}
|
|
|
|
if (trkdata.pressure[0] > 0)
|
|
printf("pressure %d\n",trkdata.pressure[0]);
|
|
printf("programcmd %d\n",trkdata.program[0]);
|
|
|
|
nprogs = 0; /* [SS] 2018-04-24 */
|
|
for (i=1;i<128;i++)
|
|
if(progactivity[i] >0) nprogs++;
|
|
if (nprogs > 0) output_progs_data();
|
|
else {
|
|
for (i=0;i<17;i++)
|
|
if(chnactivity[i] > 0)
|
|
progactivity[channel2prog[i]] = chnactivity[i];
|
|
output_progs_data();
|
|
}
|
|
|
|
determine_progcolor();
|
|
printf("\nprogcolor ");
|
|
if (npulses > 0)
|
|
for (i=0;i<17;i++) printf("%5.2f ",progcolor[i]/(double) npulses);
|
|
else
|
|
for (i=0;i<17;i++) printf("%5.2f ",(double) progcolor[i]);
|
|
printf("\n");
|
|
|
|
printf("drums ");
|
|
for (i=35;i<82;i++) {
|
|
if (drumhistogram[i] > 0) printf("%d ",i);
|
|
}
|
|
printf("\ndrumhits ");
|
|
for (i=35;i<82;i++) {
|
|
if (drumhistogram[i] > 0) printf("%d ",drumhistogram[i]);
|
|
}
|
|
|
|
printf("\npitches "); /* [SS] 2017-11-01 */
|
|
for (i=0;i<12;i++) printf("%d ",pitchhistogram[i]);
|
|
printf("\npitchact "); /* [SS] 2018-02-02 */
|
|
if (npulses > 0)
|
|
for (i=0;i<12;i++) printf("%5.2f ",pitchclass_activity[i]/(double) npulses);
|
|
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]);
|
|
else
|
|
for (i=0;i<17;i++) printf("%5.2f ",(double) chnactivity[i]);
|
|
printf("\npitchentropy %f\n",histogram_entropy(pitchclass_activity,12));
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
|
|
float histogram_entropy (int *histogram, int size)
|
|
{
|
|
int i;
|
|
int total;
|
|
float entropy;
|
|
float e,p;
|
|
total = 0;
|
|
entropy = 0.0;
|
|
for (i=0;i<size;i++) {
|
|
total += histogram[i];
|
|
}
|
|
for (i=0;i<size;i++) {
|
|
if (histogram[i] < 1) continue;
|
|
p = (float) histogram[i]/total;
|
|
e = p*log(p);
|
|
entropy = entropy + e;
|
|
}
|
|
return -entropy/log(2.0);
|
|
}
|
|
|
|
|
|
|
|
void mftxt_trackstart()
|
|
{
|
|
int numbytes;
|
|
tracknum++;
|
|
numbytes = Mf_toberead;
|
|
/*if(track != 0 && tracknum != track) {ignore_bytes(numbytes); return;} */
|
|
printf("Track %d contains %d bytes\n",tracknum,numbytes);
|
|
}
|
|
|
|
void output_count_trkdata(data_array,name)
|
|
int data_array[];
|
|
char *name;
|
|
{
|
|
int i,sum;
|
|
sum = 0;
|
|
for (i=1;i<17;i++) sum += data_array[i];
|
|
if (sum != 0) {
|
|
for (i=0;i<17;i++)
|
|
if(data_array[i]>0) {
|
|
printf("%s ",name);
|
|
printf("%d %d ",i,data_array[i]);
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void output_track_summary () {
|
|
int i;
|
|
/* find first channel containing data */
|
|
for (i=0;i<17;i++) {
|
|
if(trkdata.notecount[i] == 0 && trkdata.chordcount[i] == 0) continue;
|
|
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]);
|
|
printf("%d %d ",trkdata.cntlparam[i],trkdata.pressure[i]); /* [SS] 2022-03-04 */
|
|
printf("\n");
|
|
|
|
channel2nnotes[i] += trkdata.notecount[i] + trkdata.chordcount[i];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void stats_trackstart()
|
|
{
|
|
int i;
|
|
tracknum++;
|
|
for (i=0;i<17;i++) {
|
|
trkdata.notecount[i] = 0;
|
|
trkdata.notemeanpitch[i] = 0;
|
|
trkdata.notelength[i] = 0;
|
|
trkdata.chordcount[i] = 0;
|
|
trkdata.cntlparam[i] = 0;
|
|
last_tick[i] = -1;
|
|
last_on_tick[i] = -1;
|
|
}
|
|
printf("trk %d \n",tracknum);
|
|
}
|
|
|
|
void stats_trackend()
|
|
{
|
|
trkdata.npulses[tracknum] = Mf_currtime;
|
|
if (trkdata.npulses[0] < Mf_currtime) trkdata.npulses[0] = Mf_currtime;
|
|
output_track_summary();
|
|
}
|
|
|
|
|
|
void mftxt_noteon(chan,pitch,vol)
|
|
int chan, pitch, vol;
|
|
{
|
|
char *key;
|
|
/*
|
|
if (onlychan >=0 && chan != onlychan) return;
|
|
*/
|
|
if (prtime(timeunits)) return;
|
|
key = pitch2key(pitch);
|
|
printf("Note on %2d %2d (%3s) %3d",chan+1, pitch, key,vol);
|
|
if (chan == 9) pitch2drum(pitch);
|
|
printf("\n");
|
|
}
|
|
|
|
void stats_noteon(chan,pitch,vol)
|
|
int chan, pitch, vol;
|
|
{
|
|
if (vol == 0) {
|
|
/* treat as noteoff */
|
|
stats_noteoff(chan,pitch,vol);
|
|
return;
|
|
}
|
|
trkdata.notemeanpitch[chan+1] += pitch;
|
|
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;
|
|
last_on_tick[chan+1] = Mf_currtime; /* [SS] 2019-08-02 */
|
|
/* last_on_tick not updated by stats_noteoff */
|
|
if (chan == 9) {
|
|
if (pitch < 0 || pitch > 81)
|
|
printf("****illegal drum value %d\n",pitch);
|
|
else drumhistogram[pitch]++;
|
|
}
|
|
else pitchhistogram[pitch % 12]++; /* [SS] 2017-11-01 */
|
|
}
|
|
|
|
|
|
void mftxt_noteoff(chan,pitch,vol)
|
|
int chan, pitch, vol;
|
|
{
|
|
char *key;
|
|
/*
|
|
if (onlychan >=0 && chan != onlychan) return;
|
|
*/
|
|
if (prtime(timeunits)) return;
|
|
key = pitch2key(pitch);
|
|
printf("Note off %2d %2d (%3s) %3d\n",chan+1,pitch, key,vol);
|
|
}
|
|
|
|
|
|
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];
|
|
trkdata.notelength[chan+1] += length;
|
|
chnactivity[chan+1] += length;
|
|
if (chan == 9) return; /* drum channel */
|
|
pitchclass_activity[pitch % 12] += length;
|
|
program = trkdata.program[chan+1];
|
|
progactivity[program] += length;
|
|
/* [SS] 2018-04-18 */
|
|
if(Mf_currtime > last_tick[chan+1]) last_tick[chan+1] = Mf_currtime;
|
|
}
|
|
|
|
void mftxt_polypressure(chan,pitch,press)
|
|
int chan, pitch, press;
|
|
{
|
|
char *key;
|
|
if (prtime(timeunits)) return;
|
|
key = pitch2key(pitch);
|
|
printf("Polyphonic Key Pressure %2d %3s %3d\n",chan+1,key,press);
|
|
}
|
|
void mftxt_chanpressure(chan,pitch,press)
|
|
int chan, pitch, press;
|
|
{
|
|
char *key;
|
|
if (prtime(timeunits)) return;
|
|
key = pitch2key(pitch);
|
|
printf("Channel Pressure %2d %3s %3d\n",chan+1,key,press);
|
|
}
|
|
|
|
|
|
void mftxt_pitchbend(chan,lsb,msb)
|
|
int chan, lsb, msb;
|
|
{
|
|
float bend;
|
|
int pitchbend;
|
|
double cents;
|
|
/*
|
|
if (onlychan >=0 && chan != onlychan) return;
|
|
*/
|
|
if (prtime(timeunits)) return;
|
|
/* [SS] 2014-01-05 2015-08-04*/
|
|
pitchbend = (msb*128 + lsb);
|
|
bend = (float) (pitchbend - 8192);
|
|
cents = bend/bend2cents; /* [SS] 2022-02-12 */
|
|
printf("Pitchbend %2d %d cents = %6.3f (cents)\n",chan+1,pitchbend,cents);
|
|
}
|
|
|
|
void stats_pitchbend(chan,lsb,msb)
|
|
int chan, lsb, msb;
|
|
{
|
|
trkdata.pitchbend[0]++;
|
|
trkdata.pitchbend[chan+1]++;
|
|
}
|
|
|
|
void mftxt_program(chan,program)
|
|
int chan, program;
|
|
{
|
|
static char *patches[] = {
|
|
"Acoustic Grand","Bright Acoustic","Electric Grand","Honky-Tonk",
|
|
"Electric Piano 1","Electric Piano 2","Harpsichord","Clav",
|
|
"Celesta", "Glockenspiel", "Music Box", "Vibraphone",
|
|
"Marimba", "Xylophone", "Tubular Bells", "Dulcimer",
|
|
"Drawbar Organ", "Percussive Organ", "Rock Organ", "Church Organ",
|
|
"Reed Organ", "Accordian", "Harmonica", "Tango Accordian",
|
|
"Acoustic Guitar (nylon)", "Acoustic Guitar (steel)",
|
|
"Electric Guitar (jazz)", "Electric Guitar (clean)",
|
|
"Electric Guitar (muted)", "Overdriven Guitar",
|
|
"Distortion Guitar", "Guitar Harmonics",
|
|
"Acoustic Bass", "Electric Bass (finger)",
|
|
"Electric Bass (pick)", "Fretless Bass",
|
|
"Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2",
|
|
"Violin", "Viola", "Cello", "Contrabass",
|
|
"Tremolo Strings", "Pizzicato Strings",
|
|
"Orchestral Strings", "Timpani",
|
|
"String Ensemble 1", "String Ensemble 2",
|
|
"SynthStrings 1", "SynthStrings 2",
|
|
"Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit",
|
|
"Trumpet", "Trombone", "Tuba", "Muted Trumpet",
|
|
"French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2",
|
|
"Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax",
|
|
"Oboe", "English Horn", "Bassoon", "Clarinet",
|
|
"Piccolo", "Flute", "Recorder", "Pan Flute",
|
|
"Blown Bottle", "Skakuhachi", "Whistle", "Ocarina",
|
|
"Lead 1 (square)", "Lead 2 (sawtooth)",
|
|
"Lead 3 (calliope)", "Lead 4 (chiff)",
|
|
"Lead 5 (charang)", "Lead 6 (voice)",
|
|
"Lead 7 (fifths)", "Lead 8 (bass+lead)",
|
|
"Pad 1 (new age)", "Pad 2 (warm)",
|
|
"Pad 3 (polysynth)", "Pad 4 (choir)",
|
|
"Pad 5 (bowed)", "Pad 6 (metallic)",
|
|
"Pad 7 (halo)", "Pad 8 (sweep)",
|
|
"FX 1 (rain)", "(soundtrack)",
|
|
"FX 3 (crystal)", "FX 4 (atmosphere)",
|
|
"FX 5 (brightness)", "FX 6 (goblins)",
|
|
"FX 7 (echoes)", "FX 8 (sci-fi)",
|
|
"Sitar", "Banjo", "Shamisen", "Koto",
|
|
"Kalimba", "Bagpipe", "Fiddle", "Shanai",
|
|
"Tinkle Bell", "Agogo", "Steel Drums", "Woodblock",
|
|
"Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal",
|
|
"Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet",
|
|
"Telephone ring", "Helicopter", "Applause", "Gunshot"};
|
|
/*
|
|
if (onlychan >=0 && chan != onlychan) return;
|
|
*/
|
|
if (prtime(timeunits)) return;
|
|
printf("Program %2d %d (%s)\n",chan+1, program,patches[program]);
|
|
}
|
|
|
|
|
|
void stats_program(chan,program)
|
|
int chan, program;
|
|
{
|
|
int beatnumber;
|
|
if (program <0 || program > 127) return; /* [SS] 2018-03-06 */
|
|
if (trkdata.program[chan+1] != 0) {
|
|
beatnumber = Mf_currtime/division;
|
|
printf("cprogram %d %d %d\n",chan+1,program,beatnumber);
|
|
/* count number of times the program was modified for a channel */
|
|
trkdata.program[0] = trkdata.program[0]+1;
|
|
} else {
|
|
printf("program %d %d\n",chan+1,program);
|
|
trkdata.program[chan+1] = program;
|
|
}
|
|
if (channel2prog[chan+1]== -1) channel2prog[chan+1] = program;
|
|
}
|
|
|
|
|
|
void mftxt_parameter(chan,control,value)
|
|
int chan, control, value;
|
|
{
|
|
static char *ctype[] = {
|
|
"Bank Select", "Modulation Wheel", /*1*/
|
|
"Breath controller", "unknown", /*3*/
|
|
"Foot Pedal", "Portamento Time", /*5*/
|
|
"Data Entry", "Volume", /*7*/
|
|
"Balance", "unknown", /*9*/
|
|
"Pan position", "Expression", /*11*/
|
|
"Effect Control 1", "Effect Control 2", /*13*/
|
|
"unknown", "unknown", /*15*/
|
|
"Slider 1", "Slider 2", /*17*/
|
|
"Slider 3", "Slider 4", /*19*/
|
|
"unknown", "unknown", /*21*/
|
|
"unknown", "unknown", /*23*/
|
|
"unknown", "unknown", /*25*/
|
|
"unknown", "unknown", /*27*/
|
|
"unknown", "unknown", /*29*/
|
|
"unknown", "unknown", /*31*/
|
|
"Bank Select (fine)", "Modulation Wheel (fine)", /*33*/
|
|
"Breath controller (fine)", "unknown", /*35*/
|
|
"Foot Pedal (fine)", "Portamento Time (fine)", /*37*/
|
|
"Data Entry (fine)", "Volume (fine)", /*39*/
|
|
"Balance (fine)", "unknown", /*41*/
|
|
"Pan position (fine)", "Expression (fine)", /*43*/
|
|
"Effect Control 1 (fine)", "Effect Control 2 (fine)", /*45*/
|
|
"unknown", "unknown", /*47*/
|
|
"unknown", "unknown", /*49*/
|
|
"unknown", "unknown", /*51*/
|
|
"unknown", "unknown", /*53*/
|
|
"unknown", "unknown", /*55*/
|
|
"unknown", "unknown", /*57*/
|
|
"unknown", "unknown", /*59*/
|
|
"unknown", "unknown", /*61*/
|
|
"unknown", "unknown", /*63*/
|
|
"Hold Pedal", "Portamento", /*65*/
|
|
"Susteno Pedal", "Soft Pedal", /*67*/
|
|
"Legato Pedal", "Hold 2 Pedal", /*69*/
|
|
"Sound Variation", "Sound Timbre", /*71*/
|
|
"Sound Release Time", "Sound Attack Time", /*73*/
|
|
"Sound Brightness", "Sound Control 6", /*75*/
|
|
"Sound Control 7", "Sound Control 8", /*77*/
|
|
"Sound Control 9", "Sound Control 10", /*79*/
|
|
"GP Button 1", "GP Button 2", /*81*/
|
|
"GP Button 3", "GP Button 4", /*83*/
|
|
"unknown", "unknown", /*85*/
|
|
"unknown", "unknown", /*87*/
|
|
"unknown", "unknown", /*89*/
|
|
"unknown", "Effects Level", /*91*/
|
|
"Tremolo Level", "Chorus Level", /*93*/
|
|
"Celeste Level", "Phaser Level", /*95*/
|
|
"Data button increment", "Data button decrement", /*97*/
|
|
"NRP (fine)", "NRP (coarse)", /*99*/
|
|
"Registered parameter (fine)", "Registered parameter (coarse)", /*101*/
|
|
"unknown", "unknown", /*103*/
|
|
"unknown", "unknown", /*105*/
|
|
"unknown", "unknown", /*107*/
|
|
"unknown", "unknown", /*109*/
|
|
"unknown", "unknown", /*111*/
|
|
"unknown", "unknown", /*113*/
|
|
"unknown", "unknown", /*115*/
|
|
"unknown", "unknown", /*117*/
|
|
"unknown", "unknown", /*119*/
|
|
"All Sound Off", "All Controllers Off", /*121*/
|
|
"Local Keyboard (on/off)","All Notes Off", /*123*/
|
|
"Omni Mode Off", "Omni Mode On", /*125*/
|
|
"Mono Operation", "Poly Operation"};
|
|
|
|
/* if (onlychan >=0 && chan != onlychan) return; */
|
|
if (prtime(timeunits)) return;
|
|
|
|
if (control == 6) bend2cents = 8192.0/(100.0*value); /*[SS] 2022-02-12 */
|
|
printf("CntlParm %2d %s = %d\n",chan+1, ctype[control],value);
|
|
}
|
|
|
|
void stats_parameter(chan,control,value)
|
|
int chan, control, value;
|
|
{
|
|
/*if (control == 7) {
|
|
printf("cntrlvolume %d %d \n",chan+1,value);
|
|
}
|
|
*/
|
|
trkdata.cntlparam[chan+1]++;
|
|
}
|
|
|
|
void mftxt_metatext(type,leng,mess)
|
|
int type, leng;
|
|
char *mess;
|
|
{
|
|
static char *ttype[] = {
|
|
NULL,
|
|
"Text Event", /* type=0x01 */
|
|
"Copyright Notice", /* type=0x02 */
|
|
"Seqnce/Track Name",
|
|
"Instrument Name", /* ... */
|
|
"Lyric",
|
|
"Marker",
|
|
"Cue Point", /* type=0x07 */
|
|
"Unrecognized"
|
|
};
|
|
int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1;
|
|
int len;
|
|
register int n, c;
|
|
register char *p = mess;
|
|
|
|
if ( type < 1 || type > unrecognized )
|
|
type = unrecognized;
|
|
if (prtime(timeunits)) return;
|
|
printf("Metatext (%s) ",ttype[type]);
|
|
len = leng;
|
|
if (len > 15) len = 15;
|
|
for ( n=0; n<len; n++ ) {
|
|
c = (*p++) & 0xff;
|
|
if(iscntrl(c)) {printf(" \\0x%02x",c); continue;} /* no <cr> <lf> */
|
|
printf( (isprint(c)||isspace(c)) ? "%c" : "\\0x%02x" , c);
|
|
}
|
|
if (leng>15) printf("...");
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
void stats_metatext(type,leng,mess)
|
|
int type, leng;
|
|
char *mess;
|
|
{
|
|
int i;
|
|
if (type != 3) return;
|
|
printf("metatext %d ",type);
|
|
for (i=0;i<leng;i++) printf("%c",mess[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
void mftxt_keysig(sf,mi)
|
|
int sf, mi;
|
|
{
|
|
static char *major[] = {"Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F",
|
|
"C", "G", "D", "A", "E", "B", "F#", "C#"};
|
|
static char *minor[] = {"Abmin", "Ebmin", "Bbmin", "Fmin", "Cmin",
|
|
"Gmin", "Dmin", "Amin", "Emin", "Bmin", "F#min", "C#min", "G#min"};
|
|
int index;
|
|
index = sf + 7;
|
|
if (prtime(timeunits)) return;
|
|
if (mi)
|
|
printf("Metatext key signature %s (%d/%d)\n",minor[index],sf,mi);
|
|
else
|
|
printf("Metatext key signature %s (%d/%d)\n",major[index],sf,mi);
|
|
}
|
|
|
|
|
|
/* [SS] 2018-01-02 */
|
|
void stats_keysig(sf,mi)
|
|
int sf, mi;
|
|
{
|
|
float beatnumber;
|
|
static char *major[] = {"Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F",
|
|
"C", "G", "D", "A", "E", "B", "F#", "C#"};
|
|
static char *minor[] = {"Abmin", "Ebmin", "Bbmin", "Fmin", "Cmin",
|
|
"Gmin", "Dmin", "Amin", "Emin", "Bmin", "F#min", "C#min", "G#min"};
|
|
int index;
|
|
beatnumber = Mf_currtime/division;
|
|
index = sf + 7;
|
|
if (index < 0 || index >12) return;
|
|
if (mi)
|
|
printf("keysig %s %d %d %6.2f\n",minor[index],sf,mi,beatnumber);
|
|
else
|
|
printf("keysig %s %d %d %6.2f\n",major[index],sf,mi,beatnumber);
|
|
}
|
|
|
|
|
|
void mftxt_tempo(ltempo)
|
|
long ltempo;
|
|
{
|
|
tempo = ltempo;
|
|
if (prtime(timeunits)) return;
|
|
printf("Metatext tempo = %6.2f bpm\n",60000000.0/tempo);
|
|
}
|
|
|
|
/* [SS] 2018-01-02 */
|
|
void stats_tempo(ltempo)
|
|
long ltempo;
|
|
{
|
|
float beatnumber;
|
|
tempo = ltempo;
|
|
beatnumber = Mf_currtime/division;
|
|
if (trkdata.tempo[0] == 0) printf("tempo %6.2f bpm\n",60000000.0/tempo);
|
|
else if (trkdata.tempo[0] < 10) printf("ctempo %6.2f %6.2f\n",60000000.0/tempo,beatnumber);
|
|
trkdata.tempo[0]++;
|
|
}
|
|
|
|
void mftxt_timesig(nn,dd,cc,bb)
|
|
int nn, dd, cc, bb;
|
|
{
|
|
int denom = 1;
|
|
while ( dd-- > 0 )
|
|
denom *= 2;
|
|
if (prtime(timeunits)) return;
|
|
printf("Metatext time signature=%d/%d\n",nn,denom);
|
|
/* printf("Time signature=%d/%d MIDI-clocks/click=%d \
|
|
32nd-notes/24-MIDI-clocks=%d\n", nn,denom,cc,bb); */
|
|
}
|
|
|
|
void stats_timesig(nn,dd,cc,bb)
|
|
int nn, dd, cc, bb;
|
|
{
|
|
float beatnumber;
|
|
int denom = 1;
|
|
beatnumber = Mf_currtime/division;
|
|
while ( dd-- > 0 )
|
|
denom *= 2;
|
|
printf("timesig %d/%d %6.2f\n",nn,denom,beatnumber);
|
|
}
|
|
|
|
void mftxt_smpte(hr,mn,se,fr,ff)
|
|
int hr, mn, se, fr, ff;
|
|
{
|
|
if (prtime(timeunits)) return;
|
|
printf("Metatext SMPTE, %d:%d:%d %d=%d\n", hr,mn,se,fr,ff);
|
|
}
|
|
|
|
void mftxt_metaeot()
|
|
{
|
|
if (prtime(timeunits)) return;
|
|
printf("Meta event, end of track\n");
|
|
}
|
|
|
|
|
|
void initfunc_for_midinotes()
|
|
{
|
|
Mf_error = error;
|
|
Mf_header = print_txt_header;
|
|
Mf_trackstart = no_op0;
|
|
Mf_trackend = txt_trackend;
|
|
Mf_noteon = print_txt_noteon;
|
|
Mf_noteoff = print_txt_noteoff;
|
|
Mf_pressure = no_op3;
|
|
Mf_parameter = no_op3;
|
|
Mf_pitchbend = no_op3;
|
|
Mf_program = print_txt_program;
|
|
Mf_chanpressure = no_op3;
|
|
Mf_sysex = no_op2;
|
|
Mf_metamisc = no_op3;
|
|
Mf_seqnum = no_op1;
|
|
Mf_eot = no_op0;
|
|
Mf_timesig = no_op4;
|
|
Mf_smpte = no_op5;
|
|
Mf_tempo = no_op1;
|
|
Mf_keysig = no_op2;
|
|
Mf_seqspecific = no_op3;
|
|
Mf_text = no_op3;
|
|
Mf_arbitrary = no_op2;
|
|
}
|
|
|
|
|
|
void initfunc_for_mftext()
|
|
{
|
|
Mf_error = error;
|
|
Mf_header = mftxt_header;
|
|
Mf_trackstart = mftxt_trackstart;
|
|
Mf_trackend = txt_trackend;
|
|
Mf_noteon = mftxt_noteon;
|
|
Mf_noteoff = mftxt_noteoff;
|
|
Mf_pressure =mftxt_polypressure;
|
|
Mf_parameter = mftxt_parameter;
|
|
Mf_pitchbend = mftxt_pitchbend;
|
|
Mf_program = mftxt_program;
|
|
Mf_chanpressure = mftxt_chanpressure;
|
|
Mf_sysex = no_op2;
|
|
Mf_metamisc = no_op3;
|
|
Mf_seqnum = no_op1;
|
|
Mf_eot = mftxt_metaeot;
|
|
Mf_timesig = mftxt_timesig;
|
|
Mf_smpte = mftxt_smpte;
|
|
Mf_tempo = mftxt_tempo;
|
|
Mf_keysig = mftxt_keysig;
|
|
Mf_seqspecific = no_op3;
|
|
Mf_text = mftxt_metatext;
|
|
Mf_arbitrary = no_op2;
|
|
}
|
|
|
|
void initfunc_for_stats()
|
|
{
|
|
Mf_error = stats_error; /* [SS] 2017-11-19 */
|
|
Mf_header = stats_header;
|
|
Mf_trackstart = stats_trackstart;
|
|
Mf_trackend = stats_trackend;
|
|
Mf_noteon = stats_noteon;
|
|
Mf_noteoff = stats_noteoff;
|
|
Mf_pressure = no_op3;
|
|
Mf_parameter = stats_parameter;
|
|
Mf_pitchbend = stats_pitchbend;
|
|
Mf_program = stats_program;
|
|
Mf_chanpressure = stats_pressure;
|
|
Mf_sysex = no_op2;
|
|
Mf_metamisc = no_op3;
|
|
Mf_seqnum = no_op1;
|
|
Mf_eot = no_op0;
|
|
Mf_timesig = stats_timesig;
|
|
Mf_smpte = no_op5;
|
|
Mf_tempo = stats_tempo;
|
|
Mf_keysig = stats_keysig;
|
|
Mf_seqspecific = no_op3;
|
|
Mf_text = stats_metatext;
|
|
Mf_arbitrary = no_op2;
|
|
}
|
|
|
|
|
|
|
|
void initfuncs()
|
|
{
|
|
Mf_error = error;
|
|
Mf_header = txt_header;
|
|
Mf_trackstart = txt_trackstart;
|
|
Mf_trackend = txt_trackend;
|
|
Mf_noteon = txt_noteon;
|
|
Mf_noteoff = txt_noteoff;
|
|
Mf_pressure = txt_pressure;
|
|
Mf_parameter = txt_parameter;
|
|
Mf_pitchbend = txt_pitchbend;
|
|
Mf_program = txt_program;
|
|
Mf_chanpressure = txt_pressure;
|
|
Mf_sysex = txt_sysex;
|
|
Mf_metamisc = txt_metamisc;
|
|
Mf_seqnum = txt_metaseq;
|
|
Mf_eot = txt_metaeot;
|
|
Mf_timesig = txt_timesig;
|
|
Mf_smpte = txt_smpte;
|
|
Mf_tempo = txt_tempo;
|
|
Mf_keysig = txt_keysig;
|
|
Mf_seqspecific = txt_metaspecial;
|
|
Mf_text = txt_metatext;
|
|
Mf_arbitrary = txt_arbitrary;
|
|
}
|
|
|
|
|
|
/* Stage 2 Quantize MIDI tracks. Get key signature, time signature... */
|
|
|
|
|
|
void postprocess(trackno)
|
|
/* This routine calculates the time interval before the next note */
|
|
/* called after the MIDI file has been read in */
|
|
int trackno;
|
|
{
|
|
struct listx* i;
|
|
|
|
i = track[trackno].head;
|
|
if (i != NULL) {
|
|
track[trackno].startwait = i->note->time;
|
|
}
|
|
else {
|
|
track[trackno].startwait = 0;
|
|
};
|
|
while (i != NULL) {
|
|
if (i->next != NULL) {
|
|
i->note->dtnext = i->next->note->time - i->note->time;
|
|
}
|
|
else {
|
|
i->note->dtnext = i->note->tplay;
|
|
};
|
|
i = i->next;
|
|
};
|
|
}
|
|
|
|
void scannotes(trackno)
|
|
int trackno;
|
|
/* diagnostic routine to output notes in a track */
|
|
{
|
|
struct listx* i;
|
|
|
|
i = track[trackno].head;
|
|
while (i != NULL) {
|
|
printf("Pitch %d chan %d vel %d time %ld %ld xnum %d playnum %d\n",
|
|
i->note->pitch, i->note->chan,
|
|
i->note->vel, i->note->dtnext, i->note->tplay,
|
|
i->note->xnum, i->note->playnum);
|
|
i = i->next;
|
|
};
|
|
}
|
|
|
|
|
|
int xnum_to_next_nonchordal_note(fromitem,spare,quantum)
|
|
struct listx* fromitem;
|
|
int spare,quantum;
|
|
{
|
|
struct anote* jnote;
|
|
struct listx* nextitem;
|
|
int i,xxnum;
|
|
jnote = fromitem->note;
|
|
if (jnote->xnum > 0) return jnote->xnum;
|
|
i = 0;
|
|
nextitem = fromitem->next;
|
|
while (nextitem != NULL && i < 5) {
|
|
jnote = nextitem->note;
|
|
xxnum = (2*(jnote->dtnext + spare + (quantum/4)))/quantum;
|
|
if (xxnum > 0) return xxnum;
|
|
i++;
|
|
nextitem = nextitem->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int quantize(trackno, xunit)
|
|
/* Work out how long each note is in musical time units.
|
|
* The results are placed in note.playnum */
|
|
int trackno, xunit;
|
|
{
|
|
struct listx* j;
|
|
struct anote* this;
|
|
int spare;
|
|
int toterror;
|
|
int quantum;
|
|
int posnum,xxnum;
|
|
|
|
/* fix to avoid division by zero errors in strange MIDI */
|
|
if (xunit == 0) {
|
|
return(10000);
|
|
};
|
|
quantum = (int) (2.*xunit/parts_per_unitlen); /* xunit assume 2 parts_per_unit */
|
|
track[trackno].startunits = (2*(track[trackno].startwait + (quantum/4)))/quantum;
|
|
spare = 0;
|
|
toterror = 0;
|
|
j = track[trackno].head;
|
|
posnum = 0;
|
|
while (j != NULL) {
|
|
this = j->note;
|
|
/* this->xnum is the quantized inter onset time */
|
|
/* this->playnum is the quantized note length */
|
|
this->xnum = (2*(this->dtnext + spare + (quantum/4)))/quantum;
|
|
this->playnum = (2*(this->tplay + (quantum/4)))/quantum;
|
|
if ((this->playnum == 0) && (keep_short)) {
|
|
this->playnum = 1;
|
|
};
|
|
/* In the event of short rests, the inter onset time
|
|
* will be larger than the note length. However, for
|
|
* chords the inter onset time can be zero. */
|
|
xxnum = xnum_to_next_nonchordal_note(j,spare,quantum);
|
|
if ((swallow_rests>=0) && (xxnum - this->playnum <= restsize)
|
|
&& xxnum > 0) {
|
|
this->playnum = xxnum;
|
|
};
|
|
/* this->denom = parts_per_unitlen; this variable is never used ! */
|
|
spare = spare + this->dtnext - (this->xnum*xunit/parts_per_unitlen);
|
|
if (spare > 0) {
|
|
toterror = toterror + spare;
|
|
}
|
|
else {
|
|
toterror = toterror - spare;
|
|
};
|
|
/* gradually forget old errors so that if xunit is slightly off,
|
|
errors don't accumulate over several bars */
|
|
spare = (spare * 96)/100;
|
|
this->posnum = posnum;
|
|
posnum += this->xnum;
|
|
j = j->next;
|
|
};
|
|
return(toterror);
|
|
}
|
|
|
|
|
|
void guesslengths(trackno)
|
|
/* work out most appropriate value for a unit of musical time */
|
|
int trackno;
|
|
{
|
|
int i;
|
|
int trial[100];
|
|
float avlen, factor, tryx;
|
|
long min;
|
|
|
|
min = track[trackno].tracklen;
|
|
if (track[trackno].notes == 0) {
|
|
return;
|
|
};
|
|
avlen = ((float)(min))/((float)(track[trackno].notes));
|
|
tryx = avlen * (float) 0.75;
|
|
factor = tryx/100;
|
|
for (i=0; i<100; i++) {
|
|
trial[i] = quantize(trackno, (int) tryx);
|
|
if ((long) trial[i] < min) {
|
|
min = (long) trial[i];
|
|
xunit = (int) tryx;
|
|
};
|
|
tryx = tryx + factor;
|
|
};
|
|
xunit_set = 1;
|
|
}
|
|
|
|
|
|
int findana(maintrack, barsize)
|
|
/* work out anacrusis from MIDI */
|
|
/* look for a strong beat marking the start of a bar */
|
|
int maintrack;
|
|
int barsize;
|
|
{
|
|
int min, mincount;
|
|
int place;
|
|
struct listx* p;
|
|
|
|
min = 0;
|
|
mincount = 0;
|
|
place = 0;
|
|
p = track[maintrack].head;
|
|
while ((p != NULL) && (place < barsize)) {
|
|
if ((p->note->vel > min) && (place > 0)) {
|
|
min = p->note->vel;
|
|
mincount = place;
|
|
};
|
|
place = place + (p->note->xnum);
|
|
p = p->next;
|
|
};
|
|
return(mincount);
|
|
}
|
|
|
|
|
|
|
|
int guessana(barbeats)
|
|
int barbeats;
|
|
/* try to guess length of anacrusis */
|
|
{
|
|
int score[64];
|
|
int min, minplace;
|
|
int i,j;
|
|
|
|
if (barbeats > 64) {
|
|
fatal_error("Bar size exceeds static limit of 64 units!");
|
|
};
|
|
for (j=0; j<barbeats; j++) {
|
|
score[j] = 0;
|
|
for (i=0; i<trackcount; i++) {
|
|
score[j] = score[j] + testtrack(i, barbeats, j);
|
|
/* restore values to num */
|
|
quantize(i, xunit);
|
|
};
|
|
};
|
|
min = score[0];
|
|
minplace = 0;
|
|
for (i=0; i<barbeats; i++) {
|
|
if (score[i] < min) {
|
|
min = score[i];
|
|
minplace = i;
|
|
};
|
|
};
|
|
return(minplace);
|
|
}
|
|
|
|
|
|
int findkey(maintrack)
|
|
int maintrack;
|
|
/* work out what key MIDI file is in */
|
|
/* algorithm is simply to minimize the number of accidentals needed. */
|
|
{
|
|
int j;
|
|
int max, min, n[12], key_score[12];
|
|
int minkey, minblacks;
|
|
static int keysharps[12] = {
|
|
0, -5, 2, -3, 4, -1, 6, 1, -4, 3, -2, 5};
|
|
struct listx* p;
|
|
int thispitch;
|
|
int lastpitch;
|
|
int totalnotes;
|
|
|
|
/* analyse pitches */
|
|
/* find key */
|
|
for (j=0; j<12; j++) {
|
|
n[j] = 0;
|
|
};
|
|
min = track[maintrack].tail->note->pitch;
|
|
max = min;
|
|
totalnotes = 0;
|
|
for (j=0; j<trackcount; j++) {
|
|
totalnotes = totalnotes + track[j].notes;
|
|
p = track[j].head;
|
|
while (p != NULL) {
|
|
thispitch = p->note->pitch;
|
|
if (thispitch > max) {
|
|
max = thispitch;
|
|
}
|
|
else {
|
|
if (thispitch < min) {
|
|
min = thispitch;
|
|
};
|
|
};
|
|
n[thispitch % 12] = n[thispitch % 12] + 1;
|
|
p = p->next;
|
|
};
|
|
};
|
|
/* count black notes for each key */
|
|
/* assume pitch = 0 is C */
|
|
minkey = 0;
|
|
minblacks = totalnotes;
|
|
for (j=0; j<12; j++) {
|
|
key_score[j] = n[(j+1)%12] + n[(j+3)%12] + n[(j+6)%12] +
|
|
n[(j+8)%12] + n[(j+10)%12];
|
|
/* printf("Score for key %d is %d\n", j, key_score[j]); */
|
|
if (key_score[j] < minblacks) {
|
|
minkey = j;
|
|
minblacks = key_score[j];
|
|
};
|
|
};
|
|
/* do conversion to abc pitches */
|
|
/* Code changed to use absolute rather than */
|
|
/* relative choice of pitch for 'c' */
|
|
/* MIDDLE = (min + (max - min)/2 + 6)/12 * 12; */
|
|
/* Do last note analysis */
|
|
lastpitch = track[maintrack].tail->note->pitch;
|
|
if (minkey != (lastpitch%12)) {
|
|
fprintf(outhandle,"%% Last note suggests ");
|
|
switch((lastpitch+12-minkey)%12) {
|
|
case(2):
|
|
fprintf(outhandle,"Dorian ");
|
|
break;
|
|
case(4):
|
|
fprintf(outhandle,"Phrygian ");
|
|
break;
|
|
case(5):
|
|
fprintf(outhandle,"Lydian ");
|
|
break;
|
|
case(7):
|
|
fprintf(outhandle,"Mixolydian ");
|
|
break;
|
|
case(9):
|
|
fprintf(outhandle,"minor ");
|
|
break;
|
|
case(11):
|
|
fprintf(outhandle,"Locrian ");
|
|
break;
|
|
default:
|
|
fprintf(outhandle,"unknown ");
|
|
break;
|
|
};
|
|
fprintf(outhandle,"mode tune\n");
|
|
};
|
|
/* switch to minor mode if it gives same number of accidentals */
|
|
if ((minkey != ((lastpitch+3)%12)) &&
|
|
(key_score[minkey] == key_score[(lastpitch+3)%12])) {
|
|
minkey = (lastpitch+3)%12;
|
|
};
|
|
/* switch to major mode if it gives same number of accidentals */
|
|
if ((minkey != (lastpitch%12)) &&
|
|
(key_score[minkey] == key_score[lastpitch%12])) {
|
|
minkey = lastpitch%12;
|
|
};
|
|
sharps = keysharps[minkey];
|
|
return(sharps);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Stage 3 output MIDI tracks in abc format */
|
|
|
|
|
|
/* head and tail of list of notes in current chord playing */
|
|
/* used while abc is being generated */
|
|
struct dlistx* chordhead;
|
|
struct dlistx* chordtail;
|
|
|
|
|
|
void printchordlist()
|
|
/* diagnostic routine */
|
|
{
|
|
struct dlistx* i;
|
|
|
|
i = chordhead;
|
|
printf("----CHORD LIST------\n");
|
|
while(i != NULL) {
|
|
printf("pitch %d len %d\n", i->note->pitch, i->note->playnum);
|
|
if (i->next == i) {
|
|
fatal_error("Loopback problem!");
|
|
};
|
|
i = i->next;
|
|
};
|
|
}
|
|
|
|
void checkchordlist()
|
|
/* diagnostic routine */
|
|
/* validates data structure */
|
|
{
|
|
struct dlistx* i;
|
|
int n;
|
|
|
|
if ((chordhead == NULL) && (chordtail == NULL)) {
|
|
return;
|
|
};
|
|
if ((chordhead == NULL) && (chordtail != NULL)) {
|
|
fatal_error("chordhead == NULL and chordtail != NULL");
|
|
};
|
|
if ((chordhead != NULL) && (chordtail == NULL)) {
|
|
fatal_error("chordhead != NULL and chordtail == NULL");
|
|
};
|
|
if (chordhead->last != NULL) {
|
|
fatal_error("chordhead->last != NULL");
|
|
};
|
|
if (chordtail->next != NULL) {
|
|
fatal_error("chordtail->next != NULL");
|
|
};
|
|
i = chordhead;
|
|
n = 0;
|
|
while((i != NULL) && (i->next != NULL)) {
|
|
if (i->next->last != i) {
|
|
char msg[80];
|
|
|
|
sprintf(msg, "chordlist item %d : i->next->last!", n);
|
|
fatal_error(msg);
|
|
};
|
|
i = i->next;
|
|
n = n + 1;
|
|
};
|
|
/* checkchordlist(); */
|
|
}
|
|
|
|
void addtochord(p)
|
|
/* used when printing out abc */
|
|
struct anote* p;
|
|
{
|
|
struct dlistx* newx;
|
|
struct dlistx* place;
|
|
|
|
if (debug > 3) printf("addtochord %d\n",p->pitch);
|
|
|
|
newx = (struct dlistx*) checkmalloc(sizeof(struct dlistx));
|
|
newx->note = p;
|
|
newx->next = NULL;
|
|
newx->last = NULL;
|
|
|
|
if (chordhead == NULL) {
|
|
chordhead = newx;
|
|
chordtail = newx;
|
|
checkchordlist();
|
|
return;
|
|
};
|
|
place = chordhead;
|
|
while ((place != NULL) && (place->note->pitch > p->pitch)) {
|
|
place = place->next;
|
|
};
|
|
if (place == chordhead) {
|
|
newx->next = chordhead;
|
|
chordhead->last = newx;
|
|
chordhead = newx;
|
|
checkchordlist();
|
|
return;
|
|
};
|
|
if (place == NULL) {
|
|
newx->last = chordtail;
|
|
chordtail->next = newx;
|
|
chordtail = newx;
|
|
checkchordlist();
|
|
return;
|
|
};
|
|
newx->next = place;
|
|
newx->last = place->last;
|
|
place->last = newx;
|
|
newx->last->next = newx;
|
|
checkchordlist();
|
|
}
|
|
|
|
struct dlistx* removefromchord(i)
|
|
/* used when printing out abc */
|
|
struct dlistx* i;
|
|
{
|
|
struct dlistx* newi;
|
|
|
|
/* remove note from list */
|
|
if (i->last == NULL) {
|
|
chordhead = i->next;
|
|
}
|
|
else {
|
|
(i->last)->next = i->next;
|
|
};
|
|
if (i->next == NULL) {
|
|
chordtail = i->last;
|
|
}
|
|
else {
|
|
(i->next)->last = i->last;
|
|
};
|
|
newi = i->next;
|
|
free(i);
|
|
checkchordlist();
|
|
return(newi);
|
|
}
|
|
|
|
int findshortest(gap)
|
|
/* find the first note in the chord to terminate */
|
|
int gap;
|
|
{
|
|
int min, v;
|
|
struct dlistx* p;
|
|
|
|
p = chordhead;
|
|
min = gap;
|
|
while (p != NULL) {
|
|
v = p->note->playnum;
|
|
if (v < min) {
|
|
min = v;
|
|
};
|
|
p = p->next;
|
|
};
|
|
if (debug >3) printf("find_shortest %d --> %d\n",gap,min);
|
|
return(min);
|
|
}
|
|
|
|
void advancechord(len)
|
|
/* adjust note lengths for all notes in the chord */
|
|
int len;
|
|
{
|
|
struct dlistx* p;
|
|
|
|
if (debug > 3 && len >0) printf("\nadvancechord %d\n",len);
|
|
p = chordhead;
|
|
while (p != NULL) {
|
|
if (p->note->playnum <= len) {
|
|
if (p->note->playnum < len) {
|
|
fatal_error("Error - note too short!");
|
|
};
|
|
/* remove note */
|
|
checkchordlist();
|
|
p = removefromchord(p);
|
|
}
|
|
else {
|
|
/* shorten note */
|
|
p->note->playnum = p->note->playnum - len;
|
|
p = p->next;
|
|
};
|
|
};
|
|
}
|
|
|
|
void freshline()
|
|
/* if the current line of abc or text is non-empty, start a new line */
|
|
{
|
|
if (newline_flag) return; /* [SS] 2019-06-14 */
|
|
if (midline == 1) {
|
|
fprintf(outhandle,"\n");
|
|
midline = 0;
|
|
};
|
|
newline_flag = 1; /* [SS] 2019-06-14 */
|
|
}
|
|
|
|
|
|
void printnote (struct listx *i)
|
|
{
|
|
printf("%ld ",i->note->time);
|
|
printpitch(i->note);
|
|
printfract(i->note->playnum, parts_per_unitlen);
|
|
printf(" %d %d %d %d\n",i->note->xnum, i->note->playnum,
|
|
i->note->posnum,i->note->splitnum);
|
|
}
|
|
|
|
void listnotes(int trackno, int start, int end)
|
|
/* A diagnostic like scannotes. I usually call it when
|
|
I am in the debugger (for example in printtrack).
|
|
*/
|
|
{
|
|
struct listx* i;
|
|
int k;
|
|
i = track[trackno].head;
|
|
k = 0;
|
|
printf("ticks pitch xnum,playnum,posnum,splitnum\n");
|
|
while (i != NULL && k < end)
|
|
{
|
|
if (k >= start)
|
|
printnote(i);
|
|
k++;
|
|
i = i->next;
|
|
}
|
|
}
|
|
|
|
/* [SS] 2019-05-29 new. To see what is going on. I usually
|
|
* call it from the debugger just before printtrack().
|
|
*/
|
|
void listtrack_sizes ()
|
|
{
|
|
int i;
|
|
for (i =0;i<16;i++) {
|
|
printf("track %d = %d notes %d texts\n",i,track[i].notes,track[i].texts);
|
|
}
|
|
}
|
|
|
|
|
|
void pitch_percentiles (int trackno, int * ten, int * ninety)
|
|
{
|
|
int histogram[128];
|
|
struct listx* i;
|
|
struct anote* n;
|
|
int j,p;
|
|
float qprob,total;
|
|
for (j=0;j<127;j++) histogram[j] = 0;
|
|
i = track[trackno].head;
|
|
while (i != NULL)
|
|
{
|
|
n = i->note;
|
|
p = (int) n->pitch;
|
|
if (p >-1 && p < 127) histogram[p]++;
|
|
i = i->next;
|
|
}
|
|
/*printf("pitch histogram ");
|
|
for (j=0;j<127;j++) printf(" %d",histogram[j]);
|
|
printf("\n");
|
|
*/
|
|
for (j=0;j<127;j++)
|
|
histogram[j+1] = histogram[j+1] + histogram[j];
|
|
*ten = 0;
|
|
*ninety = 0;
|
|
if (histogram[126] == 0) return;
|
|
total = (float) histogram[126];
|
|
for (j=0;j<127;j++) {
|
|
qprob = (float) histogram[j]/total;
|
|
if (qprob < 0.1) *ten = j;
|
|
if (qprob < 0.9) *ninety = j;
|
|
}
|
|
/* printf("10 and 90 percentiles = %d %d\n",*ten,*ninety); */
|
|
}
|
|
|
|
|
|
|
|
int testtrack(trackno, barbeats, anacrusis)
|
|
/* print out one track as abc */
|
|
int trackno, barbeats, anacrusis;
|
|
{
|
|
struct listx* i;
|
|
int step, gap;
|
|
int barnotes;
|
|
int barcount;
|
|
int breakcount;
|
|
|
|
breakcount = 0;
|
|
chordhead = NULL;
|
|
chordtail = NULL;
|
|
i = track[trackno].head;
|
|
gap = 0;
|
|
if (anacrusis > 0) {
|
|
barnotes = anacrusis;
|
|
}
|
|
else {
|
|
barnotes = barbeats;
|
|
};
|
|
barcount = 0;
|
|
while((i != NULL)||(gap != 0)) {
|
|
if (gap == 0) {
|
|
/* add notes to chord */
|
|
addtochord(i->note);
|
|
gap = i->note->xnum;
|
|
i = i->next;
|
|
advancechord(0); /* get rid of any zero length notes */
|
|
}
|
|
else {
|
|
step = findshortest(gap);
|
|
if (step > barnotes) {
|
|
step = barnotes;
|
|
};
|
|
if (step == 0) {
|
|
fatal_error("Advancing by 0 in testtrack!");
|
|
};
|
|
advancechord(step);
|
|
gap = gap - step;
|
|
barnotes = barnotes - step;
|
|
if (barnotes == 0) {
|
|
if (chordhead != NULL) {
|
|
breakcount = breakcount + 1;
|
|
};
|
|
barnotes = barbeats;
|
|
barcount = barcount + 1;
|
|
if (barcount>0 && barcount%4 ==0) {
|
|
/* can't zero barcount because I use it for computing maxbarcount */
|
|
freshline();
|
|
barcount = 0;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
return(breakcount);
|
|
}
|
|
|
|
void printpitch(j)
|
|
/* convert numerical value to abc pitch */
|
|
struct anote* j;
|
|
{
|
|
int p, po,i;
|
|
|
|
p = j->pitch;
|
|
if (p == -1) {
|
|
fprintf(outhandle,"z");
|
|
}
|
|
else {
|
|
po = p % 12;
|
|
/* if ((back[trans[p]] != p) || (key[po] == 1)) { [SS] 2010-05-08 */
|
|
if (back[trans[p]] != p ) { /* [SS] 2019-05-08 */
|
|
fprintf(outhandle,"%c%c", symbol[po], atog[p]);
|
|
for (i=p%12; i<256; i += 12) /* apply accidental to all octaves */
|
|
back[trans[i]] = i;
|
|
}
|
|
else {
|
|
fprintf(outhandle,"%c", atog[p]);
|
|
};
|
|
while (p >= MIDDLE + 12) {
|
|
fprintf(outhandle,"'");
|
|
p = p - 12;
|
|
};
|
|
while (p < MIDDLE - 12) {
|
|
fprintf(outhandle,",");
|
|
p = p + 12;
|
|
};
|
|
};
|
|
}
|
|
|
|
static void reduce(a, b)
|
|
int *a, *b;
|
|
{
|
|
int t, n, m;
|
|
|
|
/* find HCF using Euclid's algorithm */
|
|
if (*a > *b) {
|
|
n = *a;
|
|
m = *b;
|
|
}
|
|
else {
|
|
n = *b;
|
|
m = *a;
|
|
};
|
|
while (m != 0) {
|
|
t = n % m;
|
|
n = m;
|
|
m = t;
|
|
};
|
|
*a = *a/n;
|
|
*b = *b/n;
|
|
}
|
|
|
|
|
|
|
|
void printfract(a, b)
|
|
/* print fraction */
|
|
/* used when printing abc */
|
|
int a, b;
|
|
{
|
|
int c, d;
|
|
|
|
c = a;
|
|
d = b;
|
|
reduce(&c,&d);
|
|
/* print out length */
|
|
if (c != 1) {
|
|
fprintf(outhandle,"%d", c);
|
|
};
|
|
if (d != 1) {
|
|
fprintf(outhandle,"/%d", d);
|
|
};
|
|
}
|
|
|
|
void printchord(len)
|
|
/* Print out the current chord. Any notes that haven't */
|
|
/* finished at the end of the chord are tied into the next chord. */
|
|
int len;
|
|
{
|
|
struct dlistx* i;
|
|
|
|
i = chordhead;
|
|
if (i == NULL) {
|
|
/* no notes in chord */
|
|
#ifdef zSPLITCODE
|
|
fprintf(outhandle,"x");
|
|
#else
|
|
fprintf(outhandle,"z");
|
|
#endif
|
|
printfract(len, parts_per_unitlen);
|
|
midline = 1;
|
|
}
|
|
else {
|
|
if (i->next == NULL) {
|
|
/* only one note in chord */
|
|
printpitch(i->note);
|
|
printfract(len, parts_per_unitlen);
|
|
midline = 1;
|
|
if (len < i->note->playnum) {
|
|
fprintf(outhandle,"-");
|
|
};
|
|
}
|
|
else {
|
|
fprintf(outhandle,"[");
|
|
while (i != NULL) {
|
|
printpitch(i->note);
|
|
/* printfract(len, parts_per_unitlen); [SS] 2020-01-06 */
|
|
if (len < i->note->playnum) {
|
|
fprintf(outhandle,"-");
|
|
};
|
|
if (nogr && i->next != NULL) fprintf(outhandle," ");
|
|
i = i->next;
|
|
};
|
|
fprintf(outhandle,"]");
|
|
printfract(len, parts_per_unitlen); /* [SS] 2020-01-06 */
|
|
midline = 1;
|
|
};
|
|
};
|
|
newline_flag = 0; /* [SS] 2019-06-14 */
|
|
}
|
|
|
|
char dospecial(i, barnotes, featurecount,allow_broken,allow_triplets)
|
|
/* identify and print out triplets and broken rhythm */
|
|
struct listx* i;
|
|
int* barnotes;
|
|
int* featurecount;
|
|
int allow_broken,allow_triplets;
|
|
{
|
|
int v1, v2, v3, vt;
|
|
int xa, xb;
|
|
int pnum;
|
|
long total, t1, t2, t3;
|
|
|
|
|
|
if ((chordhead != NULL) || (i == NULL) || (i->next == NULL)
|
|
/* || (asig%3 == 0) || (asig%2 != 0) 2004/may/09 SS*/) {
|
|
return(' ');
|
|
};
|
|
t1 = i->note->dtnext;
|
|
v1 = i->note->xnum;
|
|
pnum = i->note->playnum;
|
|
if ((v1 < pnum) || (v1 > 1 + pnum) || (pnum == 0)) {
|
|
return(' ');
|
|
};
|
|
t2 = i->next->note->dtnext;
|
|
v2 = i->next->note->xnum;
|
|
pnum = i->next->note->playnum;
|
|
if (/*(v2 < pnum) ||*/ (v2 > 1 + pnum) || (pnum == 0) || (v1+v2 > *barnotes)) {
|
|
return(' ');
|
|
};
|
|
/* look for broken rhythm */
|
|
total = t1 + t2;
|
|
if (total == 0L) {
|
|
/* shouldn't happen, but avoids possible divide by zero */
|
|
return(' ');
|
|
};
|
|
/* [SS] 2017-01-01 */
|
|
if (allow_broken == 1 && ((v1+v2)%2 == 0) && ((v1+v2)%3 != 0)) {
|
|
vt = (v1+v2)/2;
|
|
if (vt == validnote(vt)) {
|
|
/* do not try to break a note which cannot be legally expressed */
|
|
switch ((int) ((t1*6+(total/2))/total)) {
|
|
case 2:
|
|
*featurecount = 2;
|
|
i->note->xnum = vt;
|
|
i->note->playnum = vt;
|
|
i->next->note->xnum = vt;
|
|
i->next->note->playnum = vt;
|
|
return('<');
|
|
break;
|
|
case 4:
|
|
*featurecount = 2;
|
|
i->note->xnum = vt;
|
|
i->note->playnum = vt;
|
|
i->next->note->xnum = vt;
|
|
i->next->note->playnum = vt;
|
|
return('>');
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
if (allow_triplets == 0) return(' '); /* [SS] 2017-01-01 */
|
|
/* look for triplet */
|
|
if (i->next->next != NULL) {
|
|
t3 = i->next->next->note->dtnext;
|
|
v3 = i->next->next->note->xnum;
|
|
pnum = i->next->next->note->playnum;
|
|
if ((v3 < pnum) || (v3 > 1 + pnum) || (pnum == 0) ||
|
|
(v1+v2+v3 > *barnotes)) {
|
|
return(' ');
|
|
};
|
|
if ((v1+v2+v3)%2 != 0) {
|
|
return(' ');
|
|
};
|
|
vt = (v1+v2+v3)/2;
|
|
if ((vt%2 == 1) && (vt > 1)) {
|
|
/* don't want strange fractions in triplet */
|
|
return(' ');
|
|
};
|
|
total = t1+t2+t3;
|
|
xa = (int) ((t1*6+(total/2))/total);
|
|
xb = (int) (((t1+t2)*6+(total/2))/total);
|
|
if ((xa == 2) && (xb == 4) && (vt%3 != 0) ) {
|
|
*featurecount = 3;
|
|
*barnotes = *barnotes + vt;
|
|
i->note->xnum = vt;
|
|
i->note->playnum = vt;
|
|
i->next->note->xnum = vt;
|
|
i->next->note->playnum = vt;
|
|
i->next->next->note->xnum = vt;
|
|
i->next->next->note->playnum = vt;
|
|
};
|
|
};
|
|
return(' ');
|
|
}
|
|
|
|
int validnote(n)
|
|
int n;
|
|
/* work out a step which can be expressed as a musical time */
|
|
{
|
|
int v;
|
|
|
|
if (n <= 4) {
|
|
v = n;
|
|
}
|
|
else {
|
|
v = 4;
|
|
while (v*2 <= n) {
|
|
v = v*2;
|
|
};
|
|
if (v + v/2 <= n) {
|
|
v = v + v/2;
|
|
};
|
|
};
|
|
return(v);
|
|
}
|
|
|
|
void handletext(t, textplace, trackno)
|
|
/* print out text occuring in the body of the track */
|
|
/* The text is printed out at the appropriate place within the track */
|
|
/* In addition the function handles key signature and time */
|
|
/* signature changes that can occur in the middle of the tune. */
|
|
long t;
|
|
struct tlistx** textplace;
|
|
int trackno;
|
|
{
|
|
char* str;
|
|
char ch;
|
|
int type,sf,mi,nn,denom,bb;
|
|
|
|
sf = 0; /* [SS] 2021-06-27 */
|
|
|
|
while (((*textplace) != NULL) && ((*textplace)->when <= t)) {
|
|
str = (*textplace)->text;
|
|
ch = *str;
|
|
type = (*textplace)->type;
|
|
if (type == 5 && noly == 1) { /* [SS] 2019-07-12 */
|
|
*textplace = (*textplace)->next;
|
|
return;
|
|
}
|
|
|
|
remove_carriage_returns(str);
|
|
if (((int)ch == '\\') || ((int)ch == '/')) {
|
|
inkaraoke = 1;
|
|
};
|
|
if ((inkaraoke == 1) && (karaoke == 1)) {
|
|
switch(ch) {
|
|
case ' ':
|
|
fprintf(outhandle,"%s", str);
|
|
midline = 1;
|
|
break;
|
|
case '\\':
|
|
freshline();
|
|
fprintf(outhandle,"w:%s", str + 1);
|
|
midline = 1;
|
|
break;
|
|
case '/':
|
|
freshline();
|
|
fprintf(outhandle,"w:%s", str + 1);
|
|
midline = 1;
|
|
break;
|
|
default :
|
|
if (midline == 0) {
|
|
fprintf(outhandle,"%%%s", str);
|
|
}
|
|
else {
|
|
fprintf(outhandle,"-%s", str);
|
|
};
|
|
break;
|
|
};
|
|
newline_flag = 0; /* [SS] 2019-06-14 */
|
|
}
|
|
else {
|
|
freshline();
|
|
ch=*(str+1);
|
|
switch (type) {
|
|
case 0:
|
|
if (ch != '%')
|
|
fprintf(outhandle,"%%%s\n", str);
|
|
else
|
|
fprintf(outhandle,"%s\n", str);
|
|
break;
|
|
case 1: /* key signature change */
|
|
sscanf(str,"%d %d",&sf,&mi);
|
|
if((trackno != 0 || trackcount==1) &&
|
|
(active_keysig != sf)) {
|
|
setupkey(sf);
|
|
active_keysig=sf;
|
|
}
|
|
break;
|
|
case 2: /* time signature change */
|
|
sscanf(str,"%d %d %d",&nn,&denom,&bb);
|
|
if ((trackno != 0 || trackcount ==1) &&
|
|
(active_asig != nn || active_bsig != denom))
|
|
{
|
|
setup_timesig(nn,denom,bb);
|
|
fprintf(outhandle,"M: %d/%d\n",nn,denom);
|
|
fprintf(outhandle,"L: 1/%d\n",unitlen);
|
|
active_asig=nn;
|
|
active_bsig=denom;
|
|
}
|
|
break;
|
|
case 5: /* lyric [SS] 2019-07-12*/
|
|
if (ch != '%')
|
|
fprintf(outhandle,"%%%s\n", str);
|
|
else
|
|
fprintf(outhandle,"%s\n", str);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
newline_flag = 1; /* 2019-06-14 */
|
|
}
|
|
*textplace = (*textplace)->next;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* This function identifies irregular chords, (notes which
|
|
do not exactly overlap in time). The notes in the
|
|
chords are split into separate lines (split numbers).
|
|
The xnum (delay) to next note is updated.
|
|
*/
|
|
|
|
int splitstart[10],splitend[10]; /* used in forming chords from notes*/
|
|
int lastposnum[10]; /* posnum of previous note in linked list */
|
|
int endposnum; /* posnum at last note in linked list */
|
|
struct anote* prevnote[10]; /*previous note in linked list */
|
|
struct listx* last_i[10]; /*note after finishing processing bar*/
|
|
int existingsplits[10]; /* existing splits in active bar */
|
|
struct dlistx* splitchordhead[10]; /* chordhead list for splitnum */
|
|
struct dlistx* splitchordtail[10]; /* chordtail list for splitnum */
|
|
int splitgap[10]; /* gap to next note at end of split measure */
|
|
|
|
void label_split(struct anote *note, int activesplit)
|
|
{
|
|
/* The function assigns a split number (activesplit), to
|
|
a specific note, (*note). We also update splitstart
|
|
and splitend which specifies the region in time where
|
|
the another note must occur if it forms a proper chord.
|
|
After assigning a split number to the note we need to
|
|
update note->xnum as this indicates the gap to the
|
|
next note in the same split number. The function uses
|
|
a greedy algorithm. It assigns a note to the first
|
|
splitnumber code which satisfies the above constraint.
|
|
If it cannot find a splitnumber, a new one (voice or track)
|
|
is created. It would be nice if the voices kept the
|
|
high and low notes (in pitch) separate.
|
|
*/
|
|
note->splitnum = activesplit;
|
|
splitstart[activesplit] = note->posnum;
|
|
splitend[activesplit] = splitstart[activesplit] + note->playnum;
|
|
if (prevnote[activesplit])
|
|
prevnote[activesplit]->xnum = note->posnum - lastposnum[activesplit];
|
|
lastposnum[activesplit] = note->posnum;
|
|
prevnote[activesplit] = note;
|
|
/* in case this is the last activesplit note make sure it
|
|
xnum points to end of track. Otherwise it will be changed
|
|
when the next activesplit note is labeled.
|
|
*/
|
|
note->xnum = endposnum - note->posnum;
|
|
existingsplits[activesplit]++;
|
|
}
|
|
|
|
|
|
int label_split_voices (int trackno)
|
|
{
|
|
/* This function sorts all the notes in the track into
|
|
separate split part. A note is placed into a separate
|
|
part if it forms a chord in the current part but
|
|
does not have the same onset time and same end time.
|
|
If this occurs, we search for another part where
|
|
this does not happen. If we can't find such a part
|
|
a new part (split) is created.
|
|
The heuristic used needs to be improved, so
|
|
that split number 0 always contains notes and so
|
|
that notes in the same pitch range or duration are
|
|
given the same split number.
|
|
*/
|
|
int activesplit,nsplits;
|
|
int done;
|
|
struct listx* i;
|
|
int k;
|
|
int firstposnum = 0; /* [SDG] 2020-06-03 */
|
|
/* initializations */
|
|
activesplit = 0;
|
|
nsplits = 0;
|
|
if (debug > 4) printf("label_split_voices:\n");
|
|
for (k=0;k<10;k++) {
|
|
splitstart[k]=splitend[k]=lastposnum[k]=0;
|
|
prevnote[k] = NULL;
|
|
existingsplits[k] = 0;
|
|
splitgap[k]=0;
|
|
}
|
|
i = track[trackno].head;
|
|
if (track[trackno].tail == 0x0) {return 0;}
|
|
endposnum =track[trackno].tail->note->posnum +
|
|
track[trackno].tail->note->playnum;
|
|
|
|
if (i != NULL) label_split(i->note, activesplit);
|
|
/* now label all the notes in the track */
|
|
while (i != NULL)
|
|
{
|
|
done =0;
|
|
if (nsplits == 0) { /*no splits exist, create split number 0 */
|
|
activesplit = 0;
|
|
nsplits++;
|
|
i->note->splitnum = activesplit;
|
|
splitstart[activesplit] = i->note->posnum;
|
|
splitend[activesplit] = splitstart[activesplit] + i->note->playnum;
|
|
firstposnum = splitstart[activesplit];
|
|
} else { /* do a compatibility check with the last split number */
|
|
if ( ( i->note->posnum == splitstart[activesplit]
|
|
&& i->note->playnum == (splitend[activesplit] - splitstart[activesplit]))
|
|
|| i->note->posnum >= splitend[activesplit])
|
|
{
|
|
if (existingsplits[activesplit] == 0) {
|
|
last_i[activesplit] = i;
|
|
splitgap[activesplit] = i->note->posnum - firstposnum;
|
|
}
|
|
label_split(i->note, activesplit);
|
|
done = 1;
|
|
}
|
|
/* need to search for any other compatible split numbers */
|
|
if (done == 0) for (activesplit=0;activesplit<nsplits;activesplit++) {
|
|
if ( ( i->note->posnum == splitstart[activesplit]
|
|
&& i->note->playnum == splitend[activesplit] - splitstart[activesplit])
|
|
|| i->note->posnum >= splitend[activesplit])
|
|
{
|
|
if (existingsplits[activesplit] == 0) {
|
|
last_i[activesplit] = i;
|
|
splitgap[activesplit] = i->note->posnum - firstposnum;
|
|
}
|
|
label_split(i->note,activesplit);
|
|
done = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* No compatible split number found. Create new split */
|
|
if (done == 0) {
|
|
if(nsplits < 10) {nsplits++; activesplit = nsplits-1;}
|
|
if (existingsplits[activesplit] == 0) {
|
|
last_i[activesplit] = i;
|
|
splitgap[activesplit] = i->note->posnum - firstposnum;
|
|
}
|
|
label_split(i->note,activesplit);
|
|
}
|
|
}
|
|
if (debug>2) printf("note %d links to %d %d (%d %d)\n",i->note->pitch,activesplit,
|
|
i->note->posnum,splitstart[activesplit],splitend[activesplit]);
|
|
|
|
i = i->next;
|
|
} /* end while loop */
|
|
return nsplits;
|
|
}
|
|
|
|
|
|
int nextsplitnum(int splitnum)
|
|
{
|
|
while (splitnum < 9) {
|
|
splitnum++;
|
|
if (existingsplits[splitnum]) return splitnum;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int count_splits()
|
|
{
|
|
int i,n;
|
|
n = 0;
|
|
for (i=0;i<10;i++)
|
|
if (existingsplits[i]) n++;
|
|
return n;
|
|
}
|
|
|
|
|
|
/* [SS] 2019-06-26 */
|
|
int firstgap[10];
|
|
|
|
void set_first_gaps (int trackno) {
|
|
struct listx* i;
|
|
int j;
|
|
int start;
|
|
int splitnumber;
|
|
start = track[trackno].startunits;
|
|
for (j=0;j<10;j++) firstgap[j] = -1;
|
|
i = track[trackno].head;
|
|
while((i != NULL)) {
|
|
splitnumber = i->note->splitnum;
|
|
if (firstgap[splitnumber] < 0) {
|
|
firstgap[splitnumber] = start + i->note->posnum;
|
|
/* printf("split = %d gap = %d\n",splitnumber,firstgap[splitnumber]);*/
|
|
}
|
|
i = i->next;
|
|
}
|
|
}
|
|
|
|
|
|
/* [SS] 2019-06-13 */
|
|
void printtrack_split (int trackno, int splitnum, int anacrusis)
|
|
{
|
|
struct listx* i;
|
|
struct tlistx* textplace;
|
|
struct tlistx* textplace0; /* track 0 text storage */
|
|
int step, gap;
|
|
int featurecount;
|
|
int lastnote_in_split;
|
|
char broken;
|
|
int barnotes;
|
|
int barcount;
|
|
int last_barsize,barnotes_correction;
|
|
long now;
|
|
int nlines;
|
|
int bars_on_line;
|
|
|
|
featurecount = 0;
|
|
lastnote_in_split = 0;
|
|
broken = ' ';
|
|
now = 0L;
|
|
nlines= 0;
|
|
bars_on_line = 0;
|
|
chordhead = NULL; /* [SS] 2019-06-17 */
|
|
chordtail = NULL;
|
|
i = track[trackno].head;
|
|
/* gap = track[trackno].startunits; */
|
|
gap = firstgap[splitnum]; /* [SS] 2019-06-26 */
|
|
if (anacrusis > 0) {
|
|
barnotes = anacrusis;
|
|
barcount = -1;
|
|
}
|
|
else {
|
|
barnotes = barsize;
|
|
barcount = 0;
|
|
};
|
|
last_barsize = barsize;
|
|
|
|
textplace = track[trackno].texthead;
|
|
textplace0 = track[0].texthead;
|
|
fprintf(outhandle,"V: split%d%c\n",trackno+1,'A'+splitnum);
|
|
newline_flag = 1; /* [SS] 2019-06-14 */
|
|
|
|
while((i != NULL)||(gap != 0)) {
|
|
if (gap == 0) {
|
|
if (i->note->posnum + i->note->xnum == endposnum) lastnote_in_split = 1;
|
|
/* do triplet here */
|
|
if (featurecount == 0) {
|
|
if (allow_triplets || allow_broken) { /* [SS] 2017-01-01 */
|
|
broken = dospecial(i, &barnotes, &featurecount,allow_broken,allow_triplets);
|
|
};
|
|
};
|
|
/* ignore any notes that are not in the current splitnum */
|
|
if (i->note->splitnum == splitnum) {
|
|
/*printf("\nadding ");
|
|
printnote(i); */
|
|
addtochord(i->note);
|
|
gap = i->note->xnum;
|
|
now = i->note->time;
|
|
}
|
|
i = i->next;
|
|
advancechord(0); /* get rid of any zero length notes */
|
|
if (trackcount > 1 && trackno !=0 && splitnum == 0)
|
|
handletext(now, &textplace0, trackno);
|
|
/* handletext(now, &textplace,trackno); 2019-06-26 */
|
|
barnotes_correction = barsize - last_barsize;
|
|
barnotes += barnotes_correction;
|
|
last_barsize = barsize;
|
|
}
|
|
else {
|
|
step = findshortest(gap);
|
|
if (step > barnotes) {
|
|
step = barnotes;
|
|
};
|
|
step = validnote(step);
|
|
if (step == 0) {
|
|
fatal_error("Advancing by 0 in printtrack!");
|
|
};
|
|
if (featurecount == 3)
|
|
{
|
|
fprintf(outhandle," (3");
|
|
};
|
|
printchord(step);
|
|
if ( featurecount > 0) {
|
|
featurecount = featurecount - 1;
|
|
};
|
|
if ((featurecount == 1) && (broken != ' ')) {
|
|
fprintf(outhandle,"%c", broken);
|
|
};
|
|
advancechord(step);
|
|
gap = gap - step;
|
|
barnotes = barnotes - step;
|
|
if (barnotes == 0) {
|
|
nlines++;
|
|
if (nlines > 5000) {
|
|
printf("\nProbably infinite loop: aborting\n");
|
|
fprintf(outhandle,"\n\nProbably infinite loop: aborting\n");
|
|
return;
|
|
}
|
|
fprintf(outhandle,"|");
|
|
reset_back_array();
|
|
barnotes = barsize;
|
|
barcount = barcount + 1;
|
|
bars_on_line++;
|
|
if (barcount >0 && barcount%bars_per_staff == 0) {
|
|
freshline();
|
|
bars_on_line=0;
|
|
}
|
|
/* can't zero barcount because I use it for computing maxbarcount */
|
|
else if(bars_on_line >= bars_per_line && i != NULL) {
|
|
if (!lastnote_in_split) fprintf(outhandle," \\");
|
|
freshline();
|
|
bars_on_line=0;}
|
|
}
|
|
else if (featurecount == 0) {
|
|
/* note grouping algorithm */
|
|
/* [SS] 2016-07-20 waltz is a special case */
|
|
if ((barsize/parts_per_unitlen) % 3 == 0 && asig != 3) {
|
|
if ( (barnotes/parts_per_unitlen) % 3 == 0
|
|
&&(barnotes%parts_per_unitlen) == 0) {
|
|
fprintf(outhandle," ");
|
|
newline_flag = 0; /* [SS] 2019-06-14 */
|
|
};
|
|
}
|
|
else {
|
|
if (((barsize/parts_per_unitlen) % 2 == 0)
|
|
&& (barnotes % parts_per_unitlen) == 0
|
|
&& ((barnotes/parts_per_unitlen) % 2 == 0)) {
|
|
fprintf(outhandle," ");
|
|
newline_flag = 0; /* [SS] 2019-06-14 */
|
|
};
|
|
};
|
|
}
|
|
if (nogr) {fprintf(outhandle," "); newline_flag = 0;}
|
|
};
|
|
if (barcount > maxbarcount) maxbarcount = barcount;
|
|
}
|
|
freshline(); /* [SS] 2019-06-17 */
|
|
}
|
|
|
|
void printtrack_split_voice(trackno, anacrusis)
|
|
/* print out one track as abc */
|
|
int trackno, anacrusis;
|
|
{
|
|
int splitnum;
|
|
long now;
|
|
int nsplits;
|
|
int i;
|
|
struct tlistx* textplace;
|
|
nsplits = label_split_voices (trackno);
|
|
/*printf("%d splits were detected\n",nsplits);*/
|
|
set_first_gaps (trackno); /* [SS] 2019-06-26 */
|
|
textplace = track[trackno].texthead;
|
|
midline = 0;
|
|
inkaraoke = 0;
|
|
now = 0L;
|
|
active_asig = header_asig;
|
|
active_bsig = header_bsig;
|
|
setup_timesig(header_asig,header_bsig,header_bb);
|
|
active_keysig = header_keysig;
|
|
handletext(now, &textplace, trackno);
|
|
splitnum = 0;
|
|
/* gap = splitgap[splitnum]; [SS] 2019-05-12 */
|
|
/* [SS] 2019-06-13 */
|
|
for (i=0;i <nsplits;i++)
|
|
printtrack_split(trackno, i, anacrusis);
|
|
}
|
|
|
|
|
|
|
|
void printtrack(trackno, anacrusis)
|
|
/* print out one track as abc */
|
|
int trackno, anacrusis;
|
|
{
|
|
struct listx* i;
|
|
struct tlistx* textplace;
|
|
struct tlistx* textplace0; /* track 0 text storage */
|
|
int step, gap;
|
|
int barnotes;
|
|
int barcount;
|
|
int bars_on_line;
|
|
long now;
|
|
char broken;
|
|
int featurecount;
|
|
int last_barsize,barnotes_correction;
|
|
|
|
midline = 0;
|
|
featurecount = 0;
|
|
inkaraoke = 0;
|
|
now = 0L;
|
|
broken = ' ';
|
|
chordhead = NULL;
|
|
chordtail = NULL;
|
|
i = track[trackno].head;
|
|
textplace = track[trackno].texthead;
|
|
textplace0 = track[0].texthead;
|
|
gap = track[trackno].startunits;
|
|
if (anacrusis > 0) {
|
|
barnotes = anacrusis;
|
|
barcount = -1;
|
|
}
|
|
else {
|
|
barnotes = barsize;
|
|
barcount = 0;
|
|
};
|
|
bars_on_line = 0;
|
|
last_barsize = barsize;
|
|
active_asig = header_asig;
|
|
active_bsig = header_bsig;
|
|
setup_timesig(header_asig,header_bsig,header_bb);
|
|
active_keysig = header_keysig;
|
|
handletext(now, &textplace, trackno);
|
|
|
|
while((i != NULL)||(gap != 0)) {
|
|
if (debug > 4) printf("gap = %d\n",gap);
|
|
if (gap == 0) {
|
|
/* do triplet here */
|
|
if (featurecount == 0) {
|
|
if (allow_triplets || allow_broken) { /* [SS] 2017-01-01 */
|
|
broken = dospecial(i, &barnotes, &featurecount,allow_broken,allow_triplets);
|
|
};
|
|
};
|
|
/* add notes to chord */
|
|
addtochord(i->note);
|
|
gap = i->note->xnum;
|
|
now = i->note->time;
|
|
i = i->next;
|
|
advancechord(0); /* get rid of any zero length notes */
|
|
|
|
/*if (trackcount > 1 && trackno !=0)
|
|
handletext(now, &textplace0, trackno);
|
|
[SS] 2019-12-30 */
|
|
|
|
handletext(now, &textplace,trackno);
|
|
barnotes_correction = barsize - last_barsize;
|
|
barnotes += barnotes_correction;
|
|
last_barsize = barsize;
|
|
}
|
|
else {
|
|
step = findshortest(gap);
|
|
if (step > barnotes) {
|
|
step = barnotes;
|
|
};
|
|
step = validnote(step);
|
|
if (step == 0) {
|
|
fatal_error("Advancing by 0 in printtrack!");
|
|
};
|
|
if (featurecount == 3)
|
|
{
|
|
fprintf(outhandle," (3");
|
|
};
|
|
printchord(step);
|
|
if ( featurecount > 0) {
|
|
featurecount = featurecount - 1;
|
|
};
|
|
if ((featurecount == 1) && (broken != ' ')) {
|
|
fprintf(outhandle,"%c", broken);
|
|
};
|
|
advancechord(step);
|
|
gap = gap - step;
|
|
barnotes = barnotes - step;
|
|
if (barnotes == 0) {
|
|
fprintf(outhandle,"|");
|
|
reset_back_array();
|
|
barnotes = barsize;
|
|
barcount = barcount + 1;
|
|
bars_on_line++;
|
|
if (barcount >0 && barcount%bars_per_staff == 0) {
|
|
freshline();
|
|
bars_on_line=0;
|
|
}
|
|
/* can't zero barcount because I use it for computing maxbarcount */
|
|
else if(bars_on_line >= bars_per_line && i != NULL) {
|
|
fprintf(outhandle," \\");
|
|
freshline();
|
|
bars_on_line=0;}
|
|
}
|
|
else if (featurecount == 0) {
|
|
/* note grouping algorithm */
|
|
/* [SS] 2016-07-20 waltz is a special case */
|
|
if ((barsize/parts_per_unitlen) % 3 == 0 && asig != 3) {
|
|
if ( (barnotes/parts_per_unitlen) % 3 == 0
|
|
&&(barnotes%parts_per_unitlen) == 0) {
|
|
fprintf(outhandle," ");
|
|
newline_flag = 0; /* [SS] 2019-06-14 */
|
|
};
|
|
}
|
|
else {
|
|
if (((barsize/parts_per_unitlen) % 2 == 0)
|
|
&& (barnotes % parts_per_unitlen) == 0
|
|
&& ((barnotes/parts_per_unitlen) % 2 == 0)) {
|
|
fprintf(outhandle," ");
|
|
newline_flag = 0; /* [SS] 2019-06-14 */
|
|
};
|
|
};
|
|
}
|
|
if (nogr) {fprintf(outhandle," ");
|
|
newline_flag = 0;
|
|
}
|
|
};
|
|
};
|
|
/* print out all extra text */
|
|
while (textplace != NULL) {
|
|
handletext(textplace->when, &textplace, trackno);
|
|
};
|
|
freshline();
|
|
if (barcount > maxbarcount) maxbarcount = barcount;
|
|
}
|
|
|
|
|
|
|
|
|
|
void remove_carriage_returns(char *str)
|
|
{
|
|
/* a carriage return might be embedded in a midi text meta-event.
|
|
do not output this in the abc file or this would make a nonsyntactic
|
|
abc file.
|
|
*/
|
|
char * loc;
|
|
while (loc = (char *) strchr(str,'\r')) *loc = ' ';
|
|
while (loc = (char *) strchr(str,'\n')) *loc = ' ';
|
|
}
|
|
|
|
|
|
|
|
void printQ()
|
|
/* print out tempo for abc */
|
|
{
|
|
float Tnote, freq;
|
|
Tnote = mf_ticks2sec((long)((xunit*unitlen)/4), division, tempo);
|
|
freq = (float) 60.0/Tnote;
|
|
fprintf(outhandle,"Q:1/4=%d\n", (int) (freq+0.5));
|
|
if (summary>0) printf("Tempo: %d quarter notes per minute\n",
|
|
(int) (freq + 0.5));
|
|
}
|
|
|
|
|
|
|
|
/* [SS] 2019-05-08 */
|
|
void reset_back_array ()
|
|
/* reset back[] after each bar line */
|
|
{
|
|
int i;
|
|
for (i=0;i<256;i++) back[i] = barback[i];
|
|
}
|
|
|
|
void setupkey(sharps)
|
|
int sharps;
|
|
/* set up variables related to key signature */
|
|
{
|
|
char sharp[13], flat[13], shsymbol[13], flsymbol[13];
|
|
int j, t, issharp;
|
|
int minkey;
|
|
|
|
for (j=0; j<12; j++)
|
|
key[j] = 0;
|
|
minkey = (sharps+12)%12;
|
|
if (minkey%2 != 0) {
|
|
minkey = (minkey+6)%12;
|
|
};
|
|
/* [SS] 2017-12-20 changed strncpy to memcpy and limit to 12 */
|
|
/*strncpy(sharp, "ccddeffggaab",16); [SS] 2017-08-30 */
|
|
memcpy(sharp, "ccddeffggaab",12); /* [SS] 2017-12-20 */
|
|
memcpy(shsymbol, "=^=^==^=^=^=",12); /* [SS] 2017-12-20 */
|
|
if (sharps == 6) {
|
|
sharp[6] = 'e';
|
|
shsymbol[6] = '^';
|
|
};
|
|
memcpy(flat, "cddeefggaabb",12); /* [SS] 2017-12-20 */
|
|
memcpy(flsymbol, "=_=_==_=_=_=",12); /* [SS] 2017-12-20 */
|
|
/* Print out key */
|
|
|
|
if (sharps >= 0) {
|
|
if (sharps == 6) {
|
|
fprintf(outhandle,"K:F#");
|
|
}
|
|
else {
|
|
fprintf(outhandle,"K:%c", sharp[minkey] + 'A' - 'a');
|
|
};
|
|
issharp = 1;
|
|
}
|
|
else {
|
|
if (sharps == -1) {
|
|
fprintf(outhandle,"K:%c", flat[minkey] + 'A' - 'a');
|
|
}
|
|
else {
|
|
fprintf(outhandle,"K:%cb", flat[minkey] + 'A' - 'a');
|
|
};
|
|
issharp = 0;
|
|
};
|
|
if (sharps >= 0) {
|
|
fprintf(outhandle," %% %d sharps\n", sharps);
|
|
}
|
|
else {
|
|
fprintf(outhandle," %% %d flats\n", -sharps);
|
|
};
|
|
key[(minkey+1)%12] = 1;
|
|
key[(minkey+3)%12] = 1;
|
|
key[(minkey+6)%12] = 1;
|
|
key[(minkey+8)%12] = 1;
|
|
key[(minkey+10)%12] = 1;
|
|
for (j=0; j<256; j++) {
|
|
t = j%12;
|
|
if (issharp) {
|
|
atog[j] = sharp[t];
|
|
symbol[j] = shsymbol[t];
|
|
}
|
|
else {
|
|
atog[j] = flat[t];
|
|
symbol[j] = flsymbol[t];
|
|
};
|
|
trans[j] = 7*(j/12)+((int) atog[j] - 'a');
|
|
if (j < MIDDLE) {
|
|
atog[j] = (char) (int) atog[j] + 'A' - 'a';
|
|
};
|
|
if (key[t] == 0) {
|
|
barback[trans[j]] = j; /* [SS] 2019-05-08 */
|
|
};
|
|
};
|
|
reset_back_array(); /* [SS] 2019-05-08 */
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Functions for supporting the command line user interface to midi2abc. */
|
|
|
|
|
|
int readnum(num)
|
|
/* read a number from a string */
|
|
/* used for processing command line */
|
|
char *num;
|
|
{
|
|
int t;
|
|
char *p;
|
|
int neg;
|
|
|
|
t = 0;
|
|
neg = 1;
|
|
p = num;
|
|
if (*p == '-') {
|
|
p = p + 1;
|
|
neg = -1;
|
|
};
|
|
/* [JA] 2021-05-25 */
|
|
while (((int)*p >= '0') && ((int)*p <= '9') && (t < (INT_MAX-9)/10)) {
|
|
t = t * 10 + (int) *p - '0';
|
|
p = p + 1;
|
|
};
|
|
return neg*t;
|
|
}
|
|
|
|
|
|
int readnump(p)
|
|
/* read a number from a string (subtly different) */
|
|
/* used for processing command line */
|
|
char **p;
|
|
{
|
|
int t;
|
|
|
|
t = 0;
|
|
/* [JA] 2021-05-25 */
|
|
while (((int)**p >= '0') && ((int)**p <= '9') && (t < (INT_MAX-9)/10)) {
|
|
t = t * 10 + (int) **p - '0';
|
|
*p = *p + 1;
|
|
};
|
|
/* advance over any spurious extra digits */
|
|
while (isdigit(**p)) {
|
|
*p = *p + 1;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
|
|
void readsig(a, b, sig)
|
|
/* read time signature */
|
|
/* used for processing command line */
|
|
int *a, *b;
|
|
char *sig;
|
|
{
|
|
char *p;
|
|
int t;
|
|
|
|
p = sig;
|
|
if ((int)*p == 'C') {
|
|
*a = 4;
|
|
*b = 4;
|
|
return;
|
|
};
|
|
*a = readnump(&p);
|
|
if ((int)*p != '/') {
|
|
char msg[80];
|
|
|
|
sprintf(msg, "Expecting / in time signature found %c!", *p);
|
|
fatal_error(msg);
|
|
};
|
|
p = p + 1;
|
|
*b = readnump(&p);
|
|
if ((*a == 0) || (*b == 0)) {
|
|
char msg[80];
|
|
|
|
sprintf(msg, "%d/%d is not a valid time signature!", *a, *b);
|
|
fatal_error(msg);
|
|
};
|
|
t = *b;
|
|
while (t > 1) {
|
|
if (t%2 != 0) {
|
|
fatal_error("Bad key signature, divisor must be a power of 2!");
|
|
}
|
|
else {
|
|
t = t/2;
|
|
};
|
|
};
|
|
}
|
|
|
|
int is_power_of_two(int numb)
|
|
/* checks whether numb is a power of 2 less than 256 */
|
|
{
|
|
int i,k;
|
|
k = 1;
|
|
for (i= 0;i<8;i++) {
|
|
if(numb == k) return(1);
|
|
k *= 2;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int getarg(option, argc, argv)
|
|
/* extract arguments from command line */
|
|
char *option;
|
|
char *argv[];
|
|
int argc;
|
|
{
|
|
int j, place;
|
|
|
|
place = -1;
|
|
for (j=0; j<argc; j++) {
|
|
if (strcmp(option, argv[j]) == 0) {
|
|
place = j + 1;
|
|
};
|
|
};
|
|
return (place);
|
|
}
|
|
|
|
int huntfilename(argc, argv)
|
|
/* look for filename argument if -f option is missing */
|
|
/* assumes filename does not begin with '-' */
|
|
char *argv[];
|
|
int argc;
|
|
{
|
|
int j, place;
|
|
|
|
place = -1;
|
|
j = 1;
|
|
while ((place == -1) && (j < argc)) {
|
|
if (strncmp("-", argv[j], 1) != 0) {
|
|
place = j;
|
|
}
|
|
else {
|
|
if (strchr("ambQkcou", *(argv[j]+1)) == NULL) {
|
|
j = j + 1;
|
|
}
|
|
else {
|
|
j = j + 2;
|
|
};
|
|
};
|
|
};
|
|
return(place);
|
|
}
|
|
|
|
int process_command_line_arguments(argc,argv)
|
|
char *argv[];
|
|
int argc;
|
|
{
|
|
int val;
|
|
int arg;
|
|
arg = getarg("-ver",argc,argv);
|
|
if (arg != -1) {printf("%s\n",VERSION); exit(0);}
|
|
midiprint = 0;
|
|
arg = getarg("-midigram",argc,argv);
|
|
if (arg != -1)
|
|
{
|
|
midiprint = 1;
|
|
}
|
|
|
|
|
|
usesplits = 0;
|
|
arg = getarg("-splitvoices",argc,argv);
|
|
if (arg != -1)
|
|
usesplits = 2;
|
|
|
|
arg = getarg("-mftext",argc,argv);
|
|
if (arg != -1)
|
|
{
|
|
midiprint = 2;
|
|
timeunits = 2; /* [SS] 2018-10-25 */
|
|
}
|
|
|
|
arg = getarg("-mftextpulses",argc,argv);
|
|
if (arg != -1)
|
|
{
|
|
midiprint = 2;
|
|
timeunits = 3; /* [SS] 2018-10-25 */
|
|
}
|
|
|
|
|
|
|
|
arg = getarg("-stats",argc,argv);
|
|
if (arg != -1)
|
|
{
|
|
stats = 1;
|
|
}
|
|
|
|
arg = getarg("-d",argc,argv);
|
|
if ((arg != -1) && (arg <argc)) {
|
|
debug = readnum(argv[arg]);
|
|
}
|
|
|
|
arg = getarg("-a", argc, argv);
|
|
if ((arg != -1) && (arg < argc)) {
|
|
anacrusis = readnum(argv[arg]);
|
|
}
|
|
else {
|
|
anacrusis = 0;
|
|
};
|
|
arg = getarg("-m", argc, argv);
|
|
if ((arg != -1) && (arg < argc)) {
|
|
readsig(&asig, &bsig, argv[arg]);
|
|
tsig_set = 1;
|
|
}
|
|
else {
|
|
asig = 4;
|
|
bsig = 4;
|
|
tsig_set = 0;
|
|
};
|
|
arg = getarg("-Q", argc, argv);
|
|
if (arg != -1 && arg >= argc+1) { /* [SS] 2015-02-22 */
|
|
Qval = readnum(argv[arg]);
|
|
}
|
|
else {
|
|
Qval = 0;
|
|
};
|
|
arg = getarg("-u", argc,argv);
|
|
if (arg != -1) {
|
|
xunit = readnum(argv[arg]);
|
|
xunit_set = 1;
|
|
}
|
|
else {
|
|
xunit = 0;
|
|
xunit_set = 0;
|
|
};
|
|
arg = getarg("-ppu",argc,argv);
|
|
if (arg != -1) {
|
|
val = readnum(argv[arg]);
|
|
if (is_power_of_two(val)) parts_per_unitlen = val;
|
|
else {
|
|
printf("*error* -ppu parameter must be a power of 2\n");
|
|
parts_per_unitlen = 2;
|
|
}
|
|
}
|
|
else
|
|
parts_per_unitlen = 2;
|
|
arg = getarg("-aul",argc,argv);
|
|
if (arg != -1) {
|
|
val = readnum(argv[arg]);
|
|
if (is_power_of_two(val)) {
|
|
unitlen = val;
|
|
unitlen_set = 1;}
|
|
else
|
|
printf("*error* -aul parameter must be a power of 2\n");
|
|
}
|
|
arg = getarg("-bps",argc,argv);
|
|
if (arg != -1)
|
|
bars_per_staff = readnum(argv[arg]);
|
|
else {
|
|
bars_per_staff=4;
|
|
};
|
|
arg = getarg("-bpl",argc,argv);
|
|
if (arg != -1)
|
|
bars_per_line = readnum(argv[arg]);
|
|
else {
|
|
bars_per_line=1;
|
|
};
|
|
|
|
|
|
extracta = (getarg("-xa", argc, argv) != -1);
|
|
guessa = (getarg("-ga", argc, argv) != -1);
|
|
guessu = (getarg("-gu", argc, argv) != -1);
|
|
guessk = (getarg("-gk", argc, argv) != -1);
|
|
keep_short = (getarg("-s", argc, argv) != -1);
|
|
summary = getarg("-sum",argc,argv);
|
|
swallow_rests = getarg("-sr",argc,argv);
|
|
if (swallow_rests != -1) {
|
|
restsize = readnum(argv[swallow_rests]);
|
|
if(restsize <1) restsize=1;
|
|
}
|
|
obpl = getarg("-obpl",argc,argv);
|
|
if (obpl>= 0) bars_per_line=1;
|
|
if (!unitlen_set) {
|
|
if ((asig*4)/bsig >= 3) {
|
|
unitlen =8;
|
|
}
|
|
else {
|
|
unitlen = 16;
|
|
};
|
|
}
|
|
arg = getarg("-b", argc, argv);
|
|
if ((arg != -1) && (arg < argc)) {
|
|
bars = readnum(argv[arg]);
|
|
}
|
|
else {
|
|
bars = 0;
|
|
};
|
|
arg = getarg("-c", argc, argv);
|
|
if ((arg != -1) && (arg < argc)) {
|
|
xchannel = readnum(argv[arg]) - 1;
|
|
}
|
|
else {
|
|
xchannel = -1;
|
|
};
|
|
arg = getarg("-k", argc, argv);
|
|
if ((arg != -1) && (arg < argc)) {
|
|
keysig = readnum(argv[arg]);
|
|
if (keysig<-6) keysig = 12 - ((-keysig)%12);
|
|
if (keysig>6) keysig = keysig%12;
|
|
if (keysig>6) keysig = keysig - 12;
|
|
ksig_set = 1;
|
|
}
|
|
else {
|
|
keysig = -50;
|
|
ksig_set = 0;
|
|
};
|
|
|
|
if(guessk) ksig_set=1;
|
|
|
|
arg = getarg("-o",argc,argv);
|
|
if ((arg != -1) && (arg < argc)) {
|
|
outhandle = efopen(argv[arg],"w"); /* open output abc file */
|
|
}
|
|
else {
|
|
outhandle = stdout;
|
|
};
|
|
arg = getarg("-nt", argc, argv);
|
|
if (arg == -1) { /* 2017-01-01 */
|
|
allow_triplets = 1;
|
|
}
|
|
else {
|
|
allow_triplets = 0;
|
|
};
|
|
arg = getarg("-nb", argc, argv);
|
|
if (arg == -1) { /* 2017-01-01 */
|
|
allow_broken = 1;
|
|
}
|
|
else {
|
|
allow_broken = 0;
|
|
};
|
|
arg = getarg("-nogr",argc,argv);
|
|
if (arg != -1)
|
|
nogr=1;
|
|
else nogr = 0;
|
|
|
|
arg = getarg("-noly",argc,argv); /* [SS] 2019-07-12 */
|
|
if (arg != -1)
|
|
noly=1;
|
|
else noly = 0;
|
|
|
|
arg = getarg("-title",argc,argv);
|
|
if (arg != -1) {
|
|
title = addstring(argv[arg]);
|
|
}
|
|
|
|
arg = getarg("-origin",argc,argv);
|
|
if (arg != -1) {
|
|
origin = addstring(argv[arg]);
|
|
}
|
|
|
|
|
|
arg = getarg("-f", argc, argv);
|
|
if (arg == -1) {
|
|
arg = huntfilename(argc, argv);
|
|
};
|
|
|
|
if ((arg != -1) && (arg < argc)) {
|
|
F = efopen(argv[arg],"rb");
|
|
/* fprintf(outhandle,"%% input file %s\n", argv[arg]); */
|
|
}
|
|
else {
|
|
printf("midi2abc version %s\n usage :\n",VERSION);
|
|
printf("midi2abc filename <options>\n");
|
|
printf(" -a <beats in anacrusis>\n");
|
|
printf(" -xa Extract anacrusis from file ");
|
|
printf("(find first strong note)\n");
|
|
printf(" -ga Guess anacrusis (minimize ties across bars)\n");
|
|
printf(" -gk Guess key signature \n");
|
|
printf(" -gu Guess xunit from note duration statistics\n");
|
|
printf(" -m <time signature>\n");
|
|
printf(" -b <bars wanted in output>\n");
|
|
printf(" -Q <tempo in quarter-notes per minute>\n");
|
|
printf(" -k <key signature> -6 to 6 sharps\n");
|
|
printf(" -c <channel>\n");
|
|
printf(" -u <number of midi pulses in abc time unit>\n");
|
|
printf(" -ppu <number of parts in abc time unit>\n");
|
|
printf(" -aul <denominator of L: unit length>\n");
|
|
printf(" [-f] <input file>\n");
|
|
printf(" -o <output file>\n");
|
|
printf(" -s Do not discard very short notes\n");
|
|
printf(" -sr <number> Absorb short rests following note\n");
|
|
printf(" where <number> specifies its size\n");
|
|
printf(" -sum summary\n");
|
|
printf(" -nb Do not look for broken rhythms\n");
|
|
printf(" -nt Do not look for triplets or broken rhythm\n");
|
|
printf(" -bpl <number> of bars printed on one line\n");
|
|
printf(" -bps <number> of bars to be printed on staff\n");
|
|
printf(" -obpl One bar per line\n");
|
|
printf(" -nogr No note grouping. Space between all notes\n");
|
|
printf(" -noly Suppress lyric output\n");
|
|
printf(" -splitvoices splits voices to avoid nonhomophonic chords\n");
|
|
printf(" -title <string> Pastes title following\n");
|
|
printf(" -origin <string> Adds O: field containing string\n");
|
|
printf(" -midigram Prints midigram instead of abc file\n");
|
|
printf(" -mftext mftext output in beats\n");
|
|
printf(" -mftextpulses mftext output in midi pulses\n");
|
|
printf(" -mftext mftext output in seconds\n");
|
|
printf(" -stats summary and statistics output\n");
|
|
printf(" -ver version number\n");
|
|
printf(" -d <number> debug parameter\n");
|
|
printf(" None or only one of the options -aul -gu, -b, -Q -u should\n");
|
|
printf(" be specified. If none are present, midi2abc will uses the\n");
|
|
printf(" the PPQN information in the MIDI file header to determine\n");
|
|
printf(" the suitable note length. This is the recommended method.\n");
|
|
printf(" The input filename is assumed to be any string not\n");
|
|
printf(" beginning with a - (hyphen). It may be placed anywhere.\n");
|
|
|
|
exit(0);
|
|
};
|
|
return arg;
|
|
}
|
|
|
|
|
|
|
|
void midi2abc (arg, argv)
|
|
char *argv[];
|
|
int arg;
|
|
{
|
|
int voiceno;
|
|
int accidentals; /* used for printing summary */
|
|
int j;
|
|
int ten,ninety;
|
|
int argc;
|
|
|
|
|
|
/* initialization */
|
|
trackno = 0;
|
|
track[trackno].texthead = NULL;
|
|
track[trackno].texttail = NULL;
|
|
initfuncs();
|
|
playinghead = NULL;
|
|
playingtail = NULL;
|
|
karaoke = 0;
|
|
Mf_getc = filegetc;
|
|
|
|
/* parse MIDI file */
|
|
mfread();
|
|
if (debug>0) printf("mfread finished: %d tracks read\n",trackcount);
|
|
|
|
fclose(F);
|
|
|
|
/* [SS] 2019-05-29 */
|
|
/* run on all 16 tracks for midi file type 0 */
|
|
if (trackcount == 1) {
|
|
if(debug >5) listtrack_sizes();
|
|
trackcount = 16;
|
|
}
|
|
|
|
/* count MIDI tracks with notes */
|
|
maintrack = 0;
|
|
while ((track[maintrack].notes == 0) && (maintrack < trackcount)) {
|
|
maintrack = maintrack + 1;
|
|
};
|
|
if (track[maintrack].notes == 0) {
|
|
fatal_error("MIDI file has no notes!");
|
|
};
|
|
|
|
|
|
/* compute dtnext for each note */
|
|
for (j=0; j<trackcount; j++) {
|
|
postprocess(j);
|
|
};
|
|
if (debug>0) printf("postprocess run on all tracks\n");
|
|
|
|
if (tsig_set == 1){ /* for -m parameter set up time signature*/
|
|
header_asig = asig;
|
|
header_unitlen = unitlen;
|
|
header_bsig = bsig;
|
|
};
|
|
|
|
/* print abc header block */
|
|
argc = huntfilename(arg, argv);
|
|
fprintf(outhandle,"X: 1\n");
|
|
|
|
if (title != NULL)
|
|
fprintf(outhandle,"T: %s\n",title);
|
|
else
|
|
fprintf(outhandle,"T: from %s\n",argv[argc]);
|
|
|
|
if (origin != NULL)
|
|
fprintf(outhandle,"O: %s\n",origin);
|
|
|
|
if (header_bsig == 0) {
|
|
fprintf(outhandle,"%%***Missing time signature meta command in MIDI file\n");
|
|
setup_timesig(4, 4, 8);
|
|
}
|
|
fprintf(outhandle,"M: %d/%d\n", header_asig, header_bsig);
|
|
fprintf(outhandle,"L: 1/%d\n", header_unitlen);
|
|
|
|
barsize = parts_per_unitlen*header_asig*header_unitlen/header_bsig;
|
|
|
|
/* compute xunit size for -Q -b options */
|
|
if (Qval != 0) {
|
|
xunit = mf_sec2ticks((60.0*4.0)/((float)(Qval*unitlen)), division, tempo);
|
|
};
|
|
if (bars > 0) {
|
|
xunit = (int) ((float) track[maintrack].tracklen*2/(float) (bars*barsize));
|
|
/* we multiply by 2 because barsize is in half unitlen. */
|
|
};
|
|
|
|
/* estimate xunit if not set or if -gu runtime option */
|
|
if (xunit == 0 || guessu) {
|
|
guesslengths(maintrack);
|
|
};
|
|
|
|
/* output Q: (tempo) to abc file */
|
|
printQ();
|
|
|
|
/* Quantize note lengths of all tracks */
|
|
for (j=0; j<trackcount; j++) {
|
|
quantize(j, xunit);
|
|
};
|
|
|
|
/* Estimate anacrusis if requested. Otherwise it is set by */
|
|
/* user or set to 0. */
|
|
if (extracta) {
|
|
anacrusis = findana(maintrack, barsize);
|
|
fprintf(outhandle,"%%beats in anacrusis = %d\n", anacrusis);
|
|
};
|
|
if (guessa) {
|
|
anacrusis = guessana(barsize);
|
|
fprintf(outhandle,"%%beats in anacrusis = %d\n", anacrusis);
|
|
};
|
|
|
|
/* If key signature is not known find the best one. */
|
|
if ((keysig == -50 && gotkeysig ==0) || guessk) {
|
|
keysig = findkey(maintrack);
|
|
if (summary>0) printf("Best key signature = %d flats/sharps\n",keysig);
|
|
}
|
|
header_keysig = keysig;
|
|
setupkey(header_keysig);
|
|
|
|
/* scannotes(maintrack); For debugging */
|
|
if(debug>0) printf("finished getting header parameters.\n");
|
|
|
|
/* Convert each track to abc format and print */
|
|
if (trackcount > 1) {
|
|
voiceno = 1;
|
|
for (j=0; j<trackcount; j++) {
|
|
freshline();
|
|
if (track[j].notes > 0) {
|
|
if(debug > 0) printf("outputting track %d\n",j);
|
|
fprintf(outhandle,"V:%d\n", voiceno);
|
|
if (track[j].drumtrack) fprintf(outhandle,"%%%%MIDI channel 10\n");
|
|
voiceno = voiceno + 1;
|
|
pitch_percentiles(j,&ten,&ninety);
|
|
if (ten !=0) {
|
|
if(ten < 56 && ninety > 64 && ninety < 68) fprintf(outhandle,"%%%%clef bass\n");
|
|
if(ten < 56 && ninety > 67) fprintf(outhandle,"%%%%clef treble\n");
|
|
};
|
|
|
|
if (usesplits==2) printtrack_split_voice(j, anacrusis);
|
|
else printtrack(j,anacrusis);
|
|
}; /*track[j].notes > 0 */
|
|
} /* for loop */
|
|
|
|
} else { /* trackcount == 1 */
|
|
pitch_percentiles(maintrack,&ten,&ninety);
|
|
if (ten !=0) {
|
|
if(ten < 56 && ninety > 64 && ninety < 70) fprintf(outhandle,"%%%%clef bass\n");
|
|
|
|
if(ten < 56 && ninety > 69) fprintf(outhandle,"%%%%clef treble\n");
|
|
|
|
}
|
|
if (usesplits==2) printtrack_split_voice(maintrack, anacrusis);
|
|
else printtrack(maintrack,anacrusis);
|
|
};
|
|
|
|
/* scannotes(maintrack); for debugging */
|
|
|
|
/* output report if requested */
|
|
if(summary>0) {
|
|
accidentals = keysig;
|
|
if (accidentals <0 )
|
|
{
|
|
accidentals = -accidentals;
|
|
printf("Using key signature: %d flats\n", accidentals);
|
|
}
|
|
else
|
|
printf("Using key signature : %d sharps\n", accidentals);
|
|
printf("Using an anacrusis of %d beats\n",anacrusis);
|
|
printf("Using unit length : 1/%d\n",unitlen);
|
|
printf("Using %d pulses per unit length (xunit).\n",xunit);
|
|
printf("Producing %d bars of output.\n",maxbarcount);
|
|
}
|
|
|
|
|
|
/* free up data structures */
|
|
for (j=0; j< trackcount; j++) {
|
|
struct listx* this;
|
|
struct listx* x;
|
|
struct tlistx* tthis;
|
|
struct tlistx* tx;
|
|
|
|
this = track[j].head;
|
|
while (this != NULL) {
|
|
free(this->note);
|
|
x = this->next ;
|
|
free(this);
|
|
this = x;
|
|
};
|
|
tthis = track[j].texthead;
|
|
while (tthis != NULL) {
|
|
free(tthis->text);
|
|
tx = tthis->next;
|
|
free(tthis);
|
|
tthis = tx;
|
|
};
|
|
};
|
|
fclose(outhandle);
|
|
}
|
|
|
|
void midigram(argc,argv)
|
|
char *argv[];
|
|
int argc;
|
|
{
|
|
int i;
|
|
int verylasttick;
|
|
initfunc_for_midinotes();
|
|
init_notechan();
|
|
for (i=0;i<17;i++) {last_tick[i]=0;}
|
|
/*F = efopen(argv[argc -1],"rb");*/
|
|
Mf_getc = filegetc;
|
|
mfread();
|
|
verylasttick = 0;
|
|
for (i=0;i<17;i++) {
|
|
if(verylasttick < last_tick[i]) verylasttick = last_tick[i];
|
|
}
|
|
printf("%d\n",verylasttick);
|
|
}
|
|
|
|
void mftext(argc,argv)
|
|
char *argv[];
|
|
int argc;
|
|
{
|
|
int i;
|
|
initfunc_for_mftext();
|
|
init_notechan();
|
|
for (i=0;i<17;i++) {last_tick[i]=0;}
|
|
/*F = efopen(argv[argc -1],"rb");*/
|
|
Mf_getc = filegetc;
|
|
mfread();
|
|
/*printf("%d\n",last_tick);*/
|
|
}
|
|
|
|
|
|
void midistats(argc,argv)
|
|
char *argv[];
|
|
int argc;
|
|
{
|
|
initfunc_for_stats();
|
|
Mf_getc = filegetc;
|
|
mfread();
|
|
stats_finish();
|
|
}
|
|
|
|
|
|
|
|
int main(argc,argv)
|
|
char *argv[];
|
|
int argc;
|
|
{
|
|
FILE *efopen();
|
|
int arg;
|
|
|
|
arg = process_command_line_arguments(argc,argv);
|
|
if(midiprint ==1) { midigram(argc,argv);
|
|
} else if(midiprint ==2) { mftext(argc,argv);
|
|
} else if(stats == 1) { midistats(argc,argv);
|
|
} else midi2abc(argc,argv);
|
|
return 0;
|
|
}
|