mirror of
https://github.com/sshlien/abcmidi.git
synced 2026-04-19 08:13:42 +00:00
Add -PMAR option to emit MIDI marker meta-events for P: part labels (#16)
* Add -PMAR option to output `P:` information as MIDI part meta-event * Update man page and comments * Add the instance number of a part like `Part T-4` This allows better tracking of parts played more than once with complex `P:` header. * Updated CHANGES file and added author change comments in the code
This commit is contained in:
13
doc/CHANGES
13
doc/CHANGES
@@ -15675,3 +15675,16 @@ February 24 2026
|
|||||||
|
|
||||||
midistats: the summary includes ntimesig, triplets, unquantized when appropriate.
|
midistats: the summary includes ntimesig, triplets, unquantized when appropriate.
|
||||||
|
|
||||||
|
March 30 2026 [RK]
|
||||||
|
|
||||||
|
abc2midi: added -PMAR option to emit MIDI marker meta-events (0x06) for
|
||||||
|
P: part labels. When a header P: field specifies a play order (e.g.
|
||||||
|
P:(AB)3), a marker is emitted each time a section begins during the
|
||||||
|
expanded playback, with an instance number (e.g. "Part A-1", "Part B-2").
|
||||||
|
When no header P: field is present, inline P: labels in the body are
|
||||||
|
emitted as simple section markers (e.g. "Part A"). The existing code at
|
||||||
|
genmidi.c:3187 that was meant to write markers was unreachable dead code;
|
||||||
|
it has been restructured. Changes in genmidi.c (PART case in writetrack,
|
||||||
|
partmarkers global) and store.c (event_part, event_init for -PMAR flag).
|
||||||
|
Man page updated in doc/abc2midi.1.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
.SH NAME
|
.SH NAME
|
||||||
\fBabc2midi\fP \- converts abc file to MIDI file(s)
|
\fBabc2midi\fP \- converts abc file to MIDI file(s)
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
abc2midi \fIinfile\fP [\fIrefnum\fP] [\-c] [\-v] [\-ver] [\-t] [\-n limit] [\-CS] [\-quiet] [\-silent] [\-Q tempo] [\-NFNP] [\-NFER] [\-NGRA] [\-NGUI] [\-STFW] [\-OCC] [\-NCOM] [\-HARP] [\-BF] [\-TT] [\-o outfile] \-CSM [filename]
|
abc2midi \fIinfile\fP [\fIrefnum\fP] [\-c] [\-v] [\-ver] [\-t] [\-n limit] [\-CS] [\-quiet] [\-silent] [\-Q tempo] [\-NFNP] [\-NFER] [\-NGRA] [\-NGUI] [\-STFW] [\-OCC] [\-NCOM] [\-PMAR] [\-HARP] [\-BF] [\-TT] [\-o outfile] \-CSM [filename]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
The default action is to write a MIDI file for each abc tune
|
The default action is to write a MIDI file for each abc tune
|
||||||
with the filename <stem>N.mid, where <stem> is the filestem
|
with the filename <stem>N.mid, where <stem> is the filestem
|
||||||
@@ -61,6 +61,14 @@ Place lyric text in separate MIDI tracks.
|
|||||||
.B -NCOM
|
.B -NCOM
|
||||||
Suppress some comments in the output MIDI file.
|
Suppress some comments in the output MIDI file.
|
||||||
.TP
|
.TP
|
||||||
|
.B -PMAR
|
||||||
|
Emit MIDI marker meta-events for P: part labels. When a header P: field
|
||||||
|
specifies a play order (e.g. P:(AB)3), a marker is emitted each time a
|
||||||
|
section begins during the expanded playback, with an instance number
|
||||||
|
appended (e.g. "Part A-1", "Part B-1", "Part A-2", "Part B-2").
|
||||||
|
When no header P: field is present, inline P: labels in the body are
|
||||||
|
emitted as simple section markers (e.g. "Part A", "Part B").
|
||||||
|
.TP
|
||||||
.B -OCC
|
.B -OCC
|
||||||
Accept old chord convention (eg +D2G2+ instead of [DG]2).
|
Accept old chord convention (eg +D2G2+ instead of [DG]2).
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
33
genmidi.c
33
genmidi.c
@@ -105,6 +105,7 @@ int gchordbars;
|
|||||||
/* Part handling */
|
/* Part handling */
|
||||||
extern struct vstring part;
|
extern struct vstring part;
|
||||||
int parts, partno, partlabel;
|
int parts, partno, partlabel;
|
||||||
|
int partmarkers; /* -PMAR flag: emit MIDI marker meta-events for P: parts [RK] 2026-03-30 */
|
||||||
int part_start[26], part_count[26];
|
int part_start[26], part_count[26];
|
||||||
long introlen, lastlen, partlen[26];
|
long introlen, lastlen, partlen[26];
|
||||||
int partrepno;
|
int partrepno;
|
||||||
@@ -3123,17 +3124,33 @@ long writetrack(int xtrack)
|
|||||||
checksyllables();
|
checksyllables();
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case PART:
|
case PART: /* [RK] 2026-03-30 */
|
||||||
in_varend = 0;
|
in_varend = 0;
|
||||||
|
if (parts == -1) {
|
||||||
|
/* No header P: spec, body labels only: emit "Part X" */
|
||||||
|
if (partmarkers && xtrack == 0 &&
|
||||||
|
pitch[j] >= 'A' && pitch[j] <= 'Z') {
|
||||||
|
char msg[8];
|
||||||
|
snprintf(msg, sizeof(msg), "Part %c", (char) pitch[j]);
|
||||||
|
mf_write_meta_event(delta_time_track0, marker, msg, strlen(msg));
|
||||||
|
tracklen = tracklen + delta_time_track0;
|
||||||
|
delta_time_track0 = 0L;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Parts active, navigate then emit "Part X-N" marker
|
||||||
|
where N is the instance number (1-based) */
|
||||||
/*j = partbreak(xtrack, trackvoice, j); [SS] 2023.01.20 */
|
/*j = partbreak(xtrack, trackvoice, j); [SS] 2023.01.20 */
|
||||||
j = findvoice(j, trackvoice, xtrack);
|
j = findvoice(j, trackvoice, xtrack);
|
||||||
|
if (partmarkers && xtrack == 0 &&
|
||||||
if (parts == -1) {
|
partlabel >= 0 && partlabel < 26) {
|
||||||
char msg[1];
|
char msg[20];
|
||||||
|
snprintf(msg, sizeof(msg), "Part %c-%d",
|
||||||
msg[0] = (char) pitch[j];
|
(char)(partlabel + 'A'), part_count[partlabel]);
|
||||||
mf_write_meta_event(0L, marker, msg, 1);
|
mf_write_meta_event(delta_time_track0, marker, msg, strlen(msg));
|
||||||
};
|
tracklen = tracklen + delta_time_track0;
|
||||||
|
delta_time_track0 = 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case VOICE:
|
case VOICE:
|
||||||
/* search on for next occurrence of voice */
|
/* search on for next occurrence of voice */
|
||||||
|
|||||||
19
store.c
19
store.c
@@ -422,6 +422,7 @@ int apply_fermata_to_chord = 0; /* [SS] 2012-03-26 */
|
|||||||
/* Part handling */
|
/* Part handling */
|
||||||
struct vstring part;
|
struct vstring part;
|
||||||
extern int parts, partno, partlabel;
|
extern int parts, partno, partlabel;
|
||||||
|
extern int partmarkers; /* [RK] 2026-03-30 */
|
||||||
extern int part_start[26], part_count[26];
|
extern int part_start[26], part_count[26];
|
||||||
|
|
||||||
int voicesused;
|
int voicesused;
|
||||||
@@ -945,6 +946,13 @@ void event_init(int argc, char *argv[], char **filename)
|
|||||||
nocom = 0;
|
nocom = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* [RK] 2026-03-30 */
|
||||||
|
if (getarg("-PMAR", argc, argv) != -1) {
|
||||||
|
partmarkers = 1;
|
||||||
|
} else {
|
||||||
|
partmarkers = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (getarg("-STFW",argc,argv) != -1) {
|
if (getarg("-STFW",argc,argv) != -1) {
|
||||||
separate_tracks_for_words = 1;
|
separate_tracks_for_words = 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -1025,7 +1033,7 @@ void event_init(int argc, char *argv[], char **filename)
|
|||||||
printf("abc2midi version %s\n",VERSION);
|
printf("abc2midi version %s\n",VERSION);
|
||||||
printf("Usage : abc2midi <abc file> [reference number] [-c] [-v] ");
|
printf("Usage : abc2midi <abc file> [reference number] [-c] [-v] ");
|
||||||
printf("[-o filename]\n");
|
printf("[-o filename]\n");
|
||||||
printf(" [-t] [-n <value>] [-CS] [-NFNP] [-NCOM] [-NFER] [-NGRA] [-NGUI] [-HARP]\n");
|
printf(" [-t] [-n <value>] [-CS] [-NFNP] [-NCOM] [-NFER] [-NGRA] [-NGUI] [-HARP] [-PMAR]\n");
|
||||||
printf(" [reference number] selects a tune\n");
|
printf(" [reference number] selects a tune\n");
|
||||||
printf(" -c selects checking only\n");
|
printf(" -c selects checking only\n");
|
||||||
printf(" -v selects verbose option\n");
|
printf(" -v selects verbose option\n");
|
||||||
@@ -1039,6 +1047,7 @@ void event_init(int argc, char *argv[], char **filename)
|
|||||||
printf(" -Q default tempo (quarter notes/minute)\n");
|
printf(" -Q default tempo (quarter notes/minute)\n");
|
||||||
printf(" -NFNP don't process !p! or !f!-like fields\n");
|
printf(" -NFNP don't process !p! or !f!-like fields\n");
|
||||||
printf(" -NCOM suppress comments in output MIDI file\n");
|
printf(" -NCOM suppress comments in output MIDI file\n");
|
||||||
|
printf(" -PMAR emit MIDI marker meta-events for P: part labels\n"); /* [RK] 2026-03-30 */
|
||||||
printf(" -NFER ignore all fermata markings\n");
|
printf(" -NFER ignore all fermata markings\n");
|
||||||
printf(" -NGRA ignore grace notes\n");
|
printf(" -NGRA ignore grace notes\n");
|
||||||
printf(" -NGUI ignore guitar chord indications\n");
|
printf(" -NGUI ignore guitar chord indications\n");
|
||||||
@@ -2893,7 +2902,13 @@ void event_part(char *s)
|
|||||||
if (dotune) {
|
if (dotune) {
|
||||||
p = s;
|
p = s;
|
||||||
skipspace(&p);
|
skipspace(&p);
|
||||||
if (pastheader && parts == -1) return; /* [SS] 2014-04-10 */
|
if (pastheader && parts == -1) { /* [RK] 2026-03-30 */
|
||||||
|
/* No header P: spec. If -PMAR, store body P: as section label */
|
||||||
|
if (partmarkers && ((int)*p >= 'A') && ((int)*p <= 'Z')) {
|
||||||
|
addfeature(PART, (int)*p, 0, 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (pastheader) {
|
if (pastheader) {
|
||||||
if (((int)*p < 'A') || ((int)*p > 'Z')) {
|
if (((int)*p < 'A') || ((int)*p > 'Z')) {
|
||||||
if (!silent) event_error("Part must be one of A-Z");
|
if (!silent) event_error("Part must be one of A-Z");
|
||||||
|
|||||||
Reference in New Issue
Block a user