Home | History | Annotate | Line # | Download | only in midiplay
      1  1.34    rillig /*	$NetBSD: midiplay.c,v 1.34 2021/11/27 22:16:41 rillig Exp $	*/
      2   1.1  augustss 
      3   1.1  augustss /*
      4  1.16  augustss  * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc.
      5   1.1  augustss  * All rights reserved.
      6   1.1  augustss  *
      7   1.8  augustss  * This code is derived from software contributed to The NetBSD Foundation
      8  1.20      salo  * by Lennart Augustsson (augustss (at) NetBSD.org).
      9   1.1  augustss  *
     10   1.1  augustss  * Redistribution and use in source and binary forms, with or without
     11   1.1  augustss  * modification, are permitted provided that the following conditions
     12   1.1  augustss  * are met:
     13   1.1  augustss  * 1. Redistributions of source code must retain the above copyright
     14   1.1  augustss  *    notice, this list of conditions and the following disclaimer.
     15   1.1  augustss  * 2. Redistributions in binary form must reproduce the above copyright
     16   1.1  augustss  *    notice, this list of conditions and the following disclaimer in the
     17   1.1  augustss  *    documentation and/or other materials provided with the distribution.
     18   1.1  augustss  *
     19   1.1  augustss  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20   1.1  augustss  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21   1.1  augustss  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22   1.1  augustss  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23   1.1  augustss  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24   1.1  augustss  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25   1.1  augustss  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26   1.1  augustss  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27   1.1  augustss  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28   1.1  augustss  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29   1.1  augustss  * POSSIBILITY OF SUCH DAMAGE.
     30   1.1  augustss  */
     31  1.19       agc #include <sys/cdefs.h>
     32  1.19       agc 
     33  1.19       agc #ifndef lint
     34  1.34    rillig __RCSID("$NetBSD: midiplay.c,v 1.34 2021/11/27 22:16:41 rillig Exp $");
     35  1.19       agc #endif
     36  1.19       agc 
     37   1.1  augustss 
     38   1.1  augustss #include <stdio.h>
     39   1.1  augustss #include <stdlib.h>
     40   1.1  augustss #include <fcntl.h>
     41   1.1  augustss #include <err.h>
     42  1.29  jmcneill #include <errno.h>
     43  1.29  jmcneill #include <limits.h>
     44   1.1  augustss #include <unistd.h>
     45   1.1  augustss #include <string.h>
     46   1.1  augustss #include <sys/types.h>
     47   1.1  augustss #include <sys/stat.h>
     48   1.1  augustss #include <sys/ioctl.h>
     49   1.1  augustss #include <sys/midiio.h>
     50   1.1  augustss 
     51   1.1  augustss #define DEVMUSIC "/dev/music"
     52   1.1  augustss 
     53   1.1  augustss struct track {
     54  1.23      chap 	struct track *indirect; /* for fast swaps in heap code */
     55   1.1  augustss 	u_char *start, *end;
     56  1.23      chap 	u_long delta;
     57   1.1  augustss 	u_char status;
     58   1.1  augustss };
     59   1.1  augustss 
     60   1.1  augustss #define MIDI_META 0xff
     61   1.1  augustss 
     62   1.1  augustss #define META_SEQNO	0x00
     63   1.1  augustss #define META_TEXT	0x01
     64   1.1  augustss #define META_COPYRIGHT	0x02
     65   1.1  augustss #define META_TRACK	0x03
     66   1.1  augustss #define META_INSTRUMENT	0x04
     67   1.1  augustss #define META_LYRIC	0x05
     68   1.1  augustss #define META_MARKER	0x06
     69   1.1  augustss #define META_CUE	0x07
     70   1.1  augustss #define META_CHPREFIX	0x20
     71   1.1  augustss #define META_EOT	0x2f
     72   1.1  augustss #define META_SET_TEMPO	0x51
     73   1.1  augustss #define META_KEY	0x59
     74   1.1  augustss #define META_SMPTE	0x54
     75   1.1  augustss #define META_TIMESIGN	0x58
     76   1.1  augustss 
     77  1.28  christos static const char *metanames[] = {
     78  1.28  christos 	"", "Text", "Copyright", "Track", "Instrument",
     79   1.1  augustss 	"Lyric", "Marker", "Cue",
     80   1.1  augustss };
     81   1.1  augustss 
     82  1.28  christos static int midi_lengths[] = { 2, 2, 2, 2, 1, 1, 2, 0 };
     83   1.1  augustss /* Number of bytes in a MIDI command */
     84   1.1  augustss #define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7])
     85   1.1  augustss 
     86  1.28  christos #define SEQ_MK_SYSEX0(_dev,...) \
     87  1.28  christos SEQ_MK_EVENT(sysex, 0x94, .device=(_dev), .buffer={__VA_ARGS__})
     88  1.28  christos 
     89  1.28  christos 
     90  1.28  christos static void usage(void);
     91  1.28  christos static void send_event(seq_event_t *);
     92  1.28  christos static void dometa(u_int, u_char *, u_int);
     93  1.28  christos #if 0
     94  1.28  christos static void midireset(void);
     95  1.28  christos #endif
     96  1.28  christos static void send_sysex(u_char *, u_int);
     97  1.28  christos static u_long getvar(struct track *);
     98  1.28  christos static u_long getlen(struct track *);
     99  1.28  christos static void playfile(FILE *, const char *);
    100  1.28  christos static void playdata(u_char *, u_int, const char *);
    101  1.28  christos 
    102  1.28  christos static void Heapify(struct track *, int, int);
    103  1.28  christos static void BuildHeap(struct track *, int);
    104  1.28  christos static int ShrinkHeap(struct track *, int);
    105  1.23      chap 
    106  1.23      chap /*
    107  1.23      chap  * This sample plays at an apparent tempo of 120 bpm when the BASETEMPO is 150
    108  1.23      chap  * bpm, because the quavers are 5 divisions (4 on 1 off) rather than 4 total.
    109  1.23      chap  */
    110  1.28  christos #define P(c) 1, 0x90, c, 0x7f, 4, 0x80, c, 0
    111  1.28  christos #define PL(c) 1, 0x90, c, 0x7f, 8, 0x80, c, 0
    112   1.1  augustss #define C 0x3c
    113   1.1  augustss #define D 0x3e
    114   1.1  augustss #define E 0x40
    115   1.1  augustss #define F 0x41
    116   1.1  augustss 
    117  1.28  christos static u_char sample[] = {
    118  1.28  christos 	'M', 'T', 'h', 'd',  0, 0, 0, 6,  0, 1,  0, 1,  0, 8,
    119  1.28  christos 	'M', 'T', 'r', 'k',  0, 0, 0, 4+13*8,
    120  1.28  christos 	P(C), P(C), P(C), P(E), P(D), P(D), P(D),
    121   1.1  augustss 	P(F), P(E), P(E), P(D), P(D), PL(C),
    122   1.1  augustss 	0, 0xff, 0x2f, 0
    123   1.1  augustss };
    124   1.1  augustss #undef P
    125   1.1  augustss #undef PL
    126   1.1  augustss #undef C
    127   1.1  augustss #undef D
    128   1.1  augustss #undef E
    129   1.1  augustss #undef F
    130   1.1  augustss 
    131  1.31       mrg static u_char silence_sample[] = {
    132  1.31       mrg 	'M', 'T', 'h', 'd',  0, 0, 0, 6,  0, 1,  0, 1,  0, 8,
    133  1.31       mrg 	'M', 'T', 'r', 'k',  0, 0, 0, 8,
    134  1.31       mrg 	0, 0xb0, 0x78, 0x00,
    135  1.31       mrg 	0, 0xff, 0x2f, 0
    136  1.31       mrg };
    137  1.31       mrg 
    138   1.1  augustss #define MARK_HEADER "MThd"
    139   1.1  augustss #define MARK_TRACK "MTrk"
    140   1.1  augustss #define MARK_LEN 4
    141   1.1  augustss 
    142  1.18  augustss #define	RMID_SIG "RIFF"
    143  1.18  augustss #define	RMID_MIDI_ID "RMID"
    144  1.18  augustss #define	RMID_DATA_ID "data"
    145  1.18  augustss 
    146   1.1  augustss #define SIZE_LEN 4
    147   1.1  augustss #define HEADER_LEN 6
    148   1.1  augustss 
    149   1.1  augustss #define GET8(p) ((p)[0])
    150   1.1  augustss #define GET16(p) (((p)[0] << 8) | (p)[1])
    151   1.1  augustss #define GET24(p) (((p)[0] << 16) | ((p)[1] << 8) | (p)[2])
    152   1.1  augustss #define GET32(p) (((p)[0] << 24) | ((p)[1] << 16) | ((p)[2] << 8) | (p)[3])
    153  1.18  augustss #define GET32_LE(p) (((p)[3] << 24) | ((p)[2] << 16) | ((p)[1] << 8) | (p)[0])
    154   1.1  augustss 
    155  1.28  christos static void __attribute__((__noreturn__))
    156  1.16  augustss usage(void)
    157   1.1  augustss {
    158  1.32       wiz 	fprintf(stderr, "usage: %s [-lmqsvx] [-d devno] [-f file] "
    159  1.32       wiz 		"[-p pgm] [-t tempo] [file ...]\n",
    160  1.12       cgd 		getprogname());
    161   1.1  augustss 	exit(1);
    162   1.1  augustss }
    163   1.1  augustss 
    164  1.28  christos static int showmeta = 0;
    165  1.28  christos static int verbose = 0;
    166  1.23      chap #define BASETEMPO 400000		/* us/beat(=24 clks or qn) (150 bpm) */
    167  1.28  christos static u_int tempo_set = 0;
    168  1.28  christos static u_int tempo_abs = 0;
    169  1.28  christos static u_int ttempo = 100;
    170  1.28  christos static int unit = 0;
    171  1.28  christos static int play = 1;
    172  1.28  christos static int fd = -1;
    173  1.28  christos static int sameprogram = 0;
    174  1.28  christos static int insysex = 0;
    175  1.28  christos static int svsysex = 0; /* number of sysex bytes saved internally */
    176   1.1  augustss 
    177  1.28  christos static void
    178  1.23      chap send_event(seq_event_t *ev)
    179   1.1  augustss {
    180   1.3  augustss 	/*
    181   1.3  augustss 	printf("%02x %02x %02x %02x %02x %02x %02x %02x\n",
    182  1.28  christos 	       ev->arr[0], ev->arr[1], ev->arr[2], ev->arr[3],
    183   1.3  augustss 	       ev->arr[4], ev->arr[5], ev->arr[6], ev->arr[7]);
    184   1.3  augustss 	*/
    185   1.3  augustss 	if (play)
    186   1.3  augustss 		write(fd, ev, sizeof *ev);
    187   1.1  augustss }
    188   1.1  augustss 
    189  1.28  christos static u_long
    190  1.16  augustss getvar(struct track *tp)
    191   1.1  augustss {
    192   1.1  augustss 	u_long r, c;
    193   1.1  augustss 
    194   1.1  augustss 	r = 0;
    195   1.1  augustss 	do {
    196   1.1  augustss 		c = *tp->start++;
    197   1.1  augustss 		r = (r << 7) | (c & 0x7f);
    198   1.1  augustss 	} while ((c & 0x80) && tp->start < tp->end);
    199  1.23      chap 	return r;
    200  1.23      chap }
    201  1.23      chap 
    202  1.28  christos static u_long
    203  1.23      chap getlen(struct track *tp)
    204  1.23      chap {
    205  1.23      chap 	u_long len;
    206  1.23      chap 	len = getvar(tp);
    207  1.23      chap 	if (tp->start + len > tp->end)
    208  1.23      chap 		errx(1, "bogus item length exceeds remaining track size");
    209  1.23      chap 	return len;
    210   1.1  augustss }
    211   1.1  augustss 
    212  1.28  christos static void
    213  1.16  augustss dometa(u_int meta, u_char *p, u_int len)
    214   1.1  augustss {
    215  1.23      chap 	static char const * const keys[] = {
    216  1.23      chap 	        "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F",
    217  1.23      chap 		"C",
    218  1.23      chap 		"G", "D", "A", "E", "B", "F#", "C#",
    219  1.23      chap 		"G#", "D#", "A#" /* for minors */
    220  1.23      chap 	};
    221  1.23      chap 	seq_event_t ev;
    222  1.23      chap 	uint32_t usperbeat;
    223  1.28  christos 
    224   1.1  augustss 	switch (meta) {
    225   1.1  augustss 	case META_TEXT:
    226   1.1  augustss 	case META_COPYRIGHT:
    227   1.1  augustss 	case META_TRACK:
    228   1.1  augustss 	case META_INSTRUMENT:
    229   1.1  augustss 	case META_LYRIC:
    230   1.1  augustss 	case META_MARKER:
    231   1.1  augustss 	case META_CUE:
    232   1.1  augustss 		if (showmeta) {
    233   1.1  augustss 			printf("%s: ", metanames[meta]);
    234   1.1  augustss 			fwrite(p, len, 1, stdout);
    235   1.1  augustss 			printf("\n");
    236   1.1  augustss 		}
    237   1.1  augustss 		break;
    238   1.1  augustss 	case META_SET_TEMPO:
    239  1.23      chap 		usperbeat = GET24(p);
    240  1.23      chap 		ev = SEQ_MK_TIMING(TEMPO,
    241  1.23      chap 		    .bpm=(60000000. / usperbeat) * (ttempo / 100.) + 0.5);
    242   1.1  augustss 		if (showmeta)
    243  1.23      chap 			printf("Tempo: %u us/'beat'(24 midiclks)"
    244  1.23      chap 			       " at %u%%; adjusted bpm = %u\n",
    245  1.23      chap 			       usperbeat, ttempo, ev.t_TEMPO.bpm);
    246  1.23      chap 		if (tempo_abs)
    247  1.23      chap 			warnx("tempo event ignored"
    248  1.23      chap 			      " in absolute-timed MIDI file");
    249  1.23      chap 		else {
    250  1.23      chap 			send_event(&ev);
    251  1.23      chap 			if (!tempo_set) {
    252  1.23      chap 				tempo_set = 1;
    253  1.23      chap 				send_event(&SEQ_MK_TIMING(START));
    254  1.23      chap 			}
    255  1.23      chap 		}
    256   1.1  augustss 		break;
    257   1.1  augustss 	case META_TIMESIGN:
    258  1.23      chap 		ev = SEQ_MK_TIMING(TIMESIG,
    259  1.23      chap 		    .numerator=p[0],      .lg2denom=p[1],
    260  1.23      chap 		    .clks_per_click=p[2], .dsq_per_24clks=p[3]);
    261   1.1  augustss 		if (showmeta) {
    262  1.23      chap 			printf("Time signature: %d/%d."
    263  1.23      chap 			       " Click every %d midiclk%s"
    264  1.23      chap 			       " (24 midiclks = %d 32nd note%s)\n",
    265  1.23      chap 			       ev.t_TIMESIG.numerator,
    266  1.23      chap 			       1 << ev.t_TIMESIG.lg2denom,
    267  1.23      chap 			       ev.t_TIMESIG.clks_per_click,
    268  1.23      chap 			       1 == ev.t_TIMESIG.clks_per_click ? "" : "s",
    269  1.23      chap 			       ev.t_TIMESIG.dsq_per_24clks,
    270  1.23      chap 			       1 == ev.t_TIMESIG.dsq_per_24clks ? "" : "s");
    271   1.1  augustss 		}
    272  1.23      chap 		/* send_event(&ev); not implemented in sequencer */
    273   1.1  augustss 		break;
    274   1.1  augustss 	case META_KEY:
    275   1.1  augustss 		if (showmeta)
    276  1.23      chap 			printf("Key: %s %s\n",
    277  1.23      chap 			       keys[((char)p[0]) + p[1] ? 10 : 7],
    278   1.1  augustss 			       p[1] ? "minor" : "major");
    279   1.1  augustss 		break;
    280   1.1  augustss 	default:
    281   1.1  augustss 		break;
    282   1.1  augustss 	}
    283   1.1  augustss }
    284   1.1  augustss 
    285  1.28  christos #if 0
    286  1.28  christos static void
    287  1.16  augustss midireset(void)
    288   1.1  augustss {
    289   1.1  augustss 	/* General MIDI reset sequence */
    290  1.28  christos 	send_event(&SEQ_MK_SYSEX0(unit, 0x7e, 0x7f, 0x09, 0x01, 0xf7, 0xff));
    291   1.6  augustss }
    292  1.28  christos #endif
    293   1.6  augustss 
    294   1.6  augustss #define SYSEX_CHUNK 6
    295  1.28  christos static void
    296  1.16  augustss send_sysex(u_char *p, u_int l)
    297   1.6  augustss {
    298  1.23      chap 	seq_event_t event;
    299  1.23      chap 	static u_char bf[6];
    300  1.28  christos 
    301  1.28  christos 	if (0 == l) {
    302  1.23      chap 		warnx("zero-length system-exclusive event");
    303  1.23      chap 		return;
    304  1.23      chap 	}
    305  1.28  christos 
    306  1.23      chap 	/*
    307  1.23      chap 	 * This block is needed only to handle the possibility that a sysex
    308  1.23      chap 	 * message is broken into multiple events in a MIDI file that do not
    309  1.23      chap 	 * have length six; the /dev/music sequencer assumes a sysex message is
    310  1.23      chap 	 * finished with the first SYSEX event carrying fewer than six bytes,
    311  1.23      chap 	 * even if the last is not MIDI_SYSEX_END. So, we need to be careful
    312  1.23      chap 	 * not to send a short sysex event until we have seen the end byte.
    313  1.23      chap 	 * Instead, save some straggling bytes in bf, and send when we have a
    314  1.23      chap 	 * full six (or an end byte). Note bf/saved/insysex should be per-
    315  1.23      chap 	 * device, if we supported output to more than one device at a time.
    316  1.23      chap 	 */
    317  1.28  christos 	if (svsysex > 0) {
    318  1.28  christos 		if (l > sizeof bf - svsysex) {
    319  1.23      chap 			memcpy(bf + svsysex, p, sizeof bf - svsysex);
    320  1.23      chap 			l -= sizeof bf - svsysex;
    321  1.23      chap 			p += sizeof bf - svsysex;
    322  1.28  christos 			send_event(&SEQ_MK_SYSEX0(unit,
    323  1.28  christos 			    bf[0], bf[1], bf[2], bf[3], bf[4], bf[5]));
    324  1.23      chap 			svsysex = 0;
    325  1.23      chap 		} else {
    326  1.23      chap 			memcpy(bf + svsysex, p, l);
    327  1.23      chap 			svsysex += l;
    328  1.23      chap 			p += l;
    329  1.28  christos 			if (MIDI_SYSEX_END == bf[svsysex-1]) {
    330  1.23      chap 				event = SEQ_MK_SYSEX(unit);
    331  1.23      chap 				memcpy(event.sysex.buffer, bf, svsysex);
    332  1.23      chap 				send_event(&event);
    333  1.23      chap 				svsysex = insysex = 0;
    334  1.23      chap 			} else
    335  1.23      chap 				insysex = 1;
    336  1.23      chap 			return;
    337  1.23      chap 		}
    338  1.23      chap 	}
    339  1.28  christos 
    340  1.23      chap 	/*
    341  1.23      chap 	 * l > 0. May as well test now whether we will be left 'insysex'
    342  1.23      chap 	 * after processing this event.
    343  1.28  christos 	 */
    344  1.28  christos 	insysex = (MIDI_SYSEX_END != p[l-1]);
    345  1.28  christos 
    346  1.23      chap 	/*
    347  1.23      chap 	 * If not for multi-event sysexes and chunk-size weirdness, this
    348  1.23      chap 	 * function could pretty much start here. :)
    349  1.23      chap 	 */
    350  1.28  christos 	while (l >= SYSEX_CHUNK) {
    351  1.28  christos 		send_event(&SEQ_MK_SYSEX0(unit, p[0], p[1], p[2], p[3], p[4], p[5]));
    352  1.23      chap 		p += SYSEX_CHUNK;
    353  1.23      chap 		l -= SYSEX_CHUNK;
    354  1.23      chap 	}
    355  1.28  christos 	if (l > 0) {
    356  1.28  christos 		if (insysex) {
    357  1.23      chap 			memcpy(bf, p, l);
    358  1.23      chap 			svsysex = l;
    359  1.23      chap 		} else { /* a <6 byte chunk is ok if it's REALLY the end */
    360  1.23      chap 			event = SEQ_MK_SYSEX(unit);
    361  1.23      chap 			memcpy(event.sysex.buffer, p, l);
    362  1.23      chap 			send_event(&event);
    363  1.23      chap 		}
    364  1.23      chap 	}
    365   1.1  augustss }
    366   1.1  augustss 
    367  1.28  christos static void
    368  1.27     lukem playfile(FILE *f, const char *name)
    369   1.1  augustss {
    370  1.21    itojun 	u_char *buf, *nbuf;
    371   1.1  augustss 	u_int tot, n, size, nread;
    372   1.1  augustss 
    373  1.28  christos 	/*
    374   1.1  augustss 	 * We need to read the whole file into memory for easy processing.
    375   1.1  augustss 	 * Using mmap() would be nice, but some file systems do not support
    376   1.1  augustss 	 * it, nor does reading from e.g. a pipe.  The latter also precludes
    377   1.1  augustss 	 * finding out the file size without reading it.
    378   1.1  augustss 	 */
    379   1.1  augustss 	size = 1000;
    380   1.1  augustss 	buf = malloc(size);
    381   1.1  augustss 	if (buf == 0)
    382  1.17    itojun 		errx(1, "malloc() failed");
    383   1.1  augustss 	nread = size;
    384   1.1  augustss 	tot = 0;
    385   1.1  augustss 	for (;;) {
    386   1.1  augustss 		n = fread(buf + tot, 1, nread, f);
    387   1.1  augustss 		tot += n;
    388   1.1  augustss 		if (n < nread)
    389   1.1  augustss 			break;
    390   1.1  augustss 		/* There must be more to read. */
    391   1.1  augustss 		nread = size;
    392  1.21    itojun 		nbuf = realloc(buf, size * 2);
    393  1.21    itojun 		if (nbuf == NULL)
    394  1.21    itojun 			errx(1, "realloc() failed");
    395  1.21    itojun 		buf = nbuf;
    396   1.1  augustss 		size *= 2;
    397   1.1  augustss 	}
    398   1.1  augustss 	playdata(buf, tot, name);
    399   1.1  augustss 	free(buf);
    400   1.1  augustss }
    401   1.1  augustss 
    402  1.28  christos static void
    403  1.27     lukem playdata(u_char *buf, u_int tot, const char *name)
    404   1.1  augustss {
    405  1.23      chap 	int format, ntrks, divfmt, ticks, t;
    406   1.1  augustss 	u_int len, mlen, status, chan;
    407   1.1  augustss 	u_char *p, *end, byte, meta, *msg;
    408  1.29  jmcneill 	struct synth_info info;
    409   1.1  augustss 	struct track *tracks;
    410   1.1  augustss 	struct track *tp;
    411   1.1  augustss 
    412  1.29  jmcneill 	/* verify that the requested midi unit exists */
    413  1.29  jmcneill 	info.device = unit;
    414  1.30       mrg 	if (play && ioctl(fd, SEQUENCER_INFO, &info) < 0)
    415  1.29  jmcneill 		err(1, "ioctl(SEQUENCER_INFO) failed");
    416  1.29  jmcneill 
    417   1.1  augustss 	end = buf + tot;
    418  1.31       mrg 	if (verbose) {
    419  1.31       mrg 		printf("Playing %s (%d bytes)", name, tot);
    420  1.31       mrg 		if (play)
    421  1.31       mrg 			printf(" on %s (unit %d)...", info.name, info.device);
    422  1.31       mrg 		puts("\n");
    423  1.31       mrg 	}
    424   1.1  augustss 
    425  1.18  augustss 	if (tot < MARK_LEN + 4) {
    426  1.18  augustss 		warnx("Not a MIDI file, too short");
    427  1.18  augustss 		return;
    428  1.18  augustss 	}
    429  1.18  augustss 
    430  1.18  augustss 	if (memcmp(buf, RMID_SIG, MARK_LEN) == 0) {
    431  1.18  augustss 		u_char *eod;
    432  1.18  augustss 		/* Detected a RMID file, let's just check if it's
    433  1.18  augustss 		 * a MIDI file */
    434  1.27     lukem 		if ((u_int)GET32_LE(buf + MARK_LEN) != tot - 8) {
    435  1.18  augustss 			warnx("Not a RMID file, bad header");
    436  1.18  augustss 			return;
    437  1.18  augustss 		}
    438  1.18  augustss 
    439  1.18  augustss 		buf += MARK_LEN + 4;
    440  1.18  augustss 		if (memcmp(buf, RMID_MIDI_ID, MARK_LEN) != 0) {
    441  1.18  augustss 			warnx("Not a RMID file, bad ID");
    442  1.18  augustss 			return;
    443  1.18  augustss 		}
    444  1.18  augustss 
    445  1.18  augustss 		/* Now look for the 'data' chunk, which contains
    446  1.18  augustss 		 * MIDI data */
    447  1.18  augustss 		buf += MARK_LEN;
    448  1.18  augustss 
    449  1.18  augustss 		/* Test against end-8 since we must have at least 8 bytes
    450  1.18  augustss 		 * left to read */
    451  1.18  augustss 		while(buf < end-8 && memcmp(buf, RMID_DATA_ID, MARK_LEN))
    452  1.18  augustss 			buf += GET32_LE(buf+4) + 8; /* MARK_LEN + 4 */
    453  1.18  augustss 
    454  1.18  augustss 		if (buf >= end-8) {
    455  1.18  augustss 			warnx("Not a valid RMID file, no data chunk");
    456  1.18  augustss 			return;
    457  1.18  augustss 		}
    458  1.18  augustss 
    459  1.18  augustss 		buf += MARK_LEN; /* "data" */
    460  1.18  augustss 		eod = buf + 4 + GET32_LE(buf);
    461  1.18  augustss 		if (eod >= end) {
    462  1.18  augustss 			warnx("Not a valid RMID file, bad data chunk size");
    463  1.18  augustss 			return;
    464  1.18  augustss 		}
    465  1.18  augustss 
    466  1.18  augustss 		end = eod;
    467  1.18  augustss 		buf += 4;
    468  1.18  augustss 	}
    469  1.18  augustss 
    470   1.1  augustss 	if (memcmp(buf, MARK_HEADER, MARK_LEN) != 0) {
    471  1.17    itojun 		warnx("Not a MIDI file, missing header");
    472   1.1  augustss 		return;
    473   1.1  augustss 	}
    474  1.18  augustss 
    475   1.1  augustss 	if (GET32(buf + MARK_LEN) != HEADER_LEN) {
    476  1.17    itojun 		warnx("Not a MIDI file, bad header");
    477   1.1  augustss 		return;
    478   1.1  augustss 	}
    479   1.1  augustss 	format = GET16(buf + MARK_LEN + SIZE_LEN);
    480   1.1  augustss 	ntrks = GET16(buf + MARK_LEN + SIZE_LEN + 2);
    481   1.1  augustss 	divfmt = GET8(buf + MARK_LEN + SIZE_LEN + 4);
    482   1.1  augustss 	ticks = GET8(buf + MARK_LEN + SIZE_LEN + 5);
    483   1.1  augustss 	p = buf + MARK_LEN + SIZE_LEN + HEADER_LEN;
    484  1.23      chap 	/*
    485  1.23      chap 	 * Set the timebase (or timebase and tempo, for absolute-timed files).
    486  1.23      chap 	 * PORTABILITY: some sequencers actually check the timebase against
    487  1.23      chap 	 * available timing sources and may adjust it accordingly (storing a
    488  1.23      chap 	 * new value in the ioctl arg) which would require us to compensate
    489  1.23      chap 	 * somehow. That possibility is ignored for now, as NetBSD's sequencer
    490  1.23      chap 	 * currently synthesizes all timebases, for better or worse, from the
    491  1.23      chap 	 * system clock.
    492  1.23      chap 	 *
    493  1.23      chap 	 * For a non-absolute file, if timebase is set to the file's divisions
    494  1.23      chap 	 * value, and tempo set in the obvious way, then the timing deltas in
    495  1.23      chap 	 * the MTrks require no scaling. A downside to this approach is that
    496  1.23      chap 	 * the sequencer API wants tempo in (integer) beats per minute, which
    497  1.23      chap 	 * limits how finely tempo can be specified. That might be got around
    498  1.23      chap 	 * in some cases by frobbing tempo and timebase more obscurely, but this
    499  1.23      chap 	 * player is meant to be simple and clear.
    500  1.23      chap 	 */
    501  1.30       mrg 	if (!play)
    502  1.30       mrg 		/* do nothing */;
    503  1.30       mrg 	else if ((divfmt & 0x80) == 0) {
    504   1.1  augustss 		ticks |= divfmt << 8;
    505  1.23      chap 		if (ioctl(fd, SEQUENCER_TMR_TIMEBASE, &(int){ticks}) < 0)
    506  1.23      chap 			err(1, "SEQUENCER_TMR_TIMEBASE");
    507  1.23      chap 	} else {
    508  1.23      chap 		tempo_abs = tempo_set = 1;
    509  1.23      chap 		divfmt = -(int8_t)divfmt;
    510  1.23      chap 		/*
    511  1.23      chap 		 * divfmt is frames per second; multiplying by 60 to set tempo
    512  1.23      chap 		 * in frames per minute could exceed sequencer's (arbitrary)
    513  1.23      chap 		 * tempo limits, so factor 60 as 12*5, set tempo in frames per
    514  1.23      chap 		 * 12 seconds, and account for the 5 in timebase.
    515  1.23      chap 		 */
    516  1.23      chap 		send_event(&SEQ_MK_TIMING(TEMPO,
    517  1.23      chap 		    .bpm=(12*divfmt) * (ttempo/100.) + 0.5));
    518  1.23      chap 		if (ioctl(fd, SEQUENCER_TMR_TIMEBASE, &(int){5*ticks}) < 0)
    519  1.23      chap 			err(1, "SEQUENCER_TMR_TIMEBASE");
    520  1.23      chap 	}
    521   1.1  augustss 	if (verbose > 1)
    522  1.23      chap 		printf(tempo_abs ?
    523  1.23      chap 		       "format=%d ntrks=%d abs fps=%u subdivs=%u\n" :
    524  1.23      chap 		       "format=%d ntrks=%d divisions=%u\n",
    525  1.23      chap 		       format, ntrks, tempo_abs ? divfmt : ticks, ticks);
    526   1.1  augustss 	if (format != 0 && format != 1) {
    527  1.17    itojun 		warnx("Cannot play MIDI file of type %d", format);
    528   1.1  augustss 		return;
    529   1.1  augustss 	}
    530   1.1  augustss 	if (ntrks == 0)
    531   1.1  augustss 		return;
    532   1.1  augustss 	tracks = malloc(ntrks * sizeof(struct track));
    533   1.1  augustss 	if (tracks == NULL)
    534  1.17    itojun 		errx(1, "malloc() tracks failed");
    535  1.28  christos 	for (t = 0; t < ntrks;) {
    536   1.1  augustss 		if (p >= end - MARK_LEN - SIZE_LEN) {
    537  1.17    itojun 			warnx("Cannot find track %d", t);
    538   1.6  augustss 			goto ret;
    539   1.1  augustss 		}
    540   1.1  augustss 		len = GET32(p + MARK_LEN);
    541   1.1  augustss 		if (len > 1000000) { /* a safe guard */
    542  1.17    itojun 			warnx("Crazy track length");
    543   1.6  augustss 			goto ret;
    544   1.1  augustss 		}
    545   1.1  augustss 		if (memcmp(p, MARK_TRACK, MARK_LEN) == 0) {
    546   1.1  augustss 			tracks[t].start = p + MARK_LEN + SIZE_LEN;
    547   1.1  augustss 			tracks[t].end = tracks[t].start + len;
    548  1.23      chap 			tracks[t].delta = getvar(&tracks[t]);
    549  1.23      chap 			tracks[t].indirect = &tracks[t]; /* -> self for now */
    550   1.1  augustss 			t++;
    551   1.1  augustss 		}
    552   1.1  augustss 		p += MARK_LEN + SIZE_LEN + len;
    553   1.1  augustss 	}
    554   1.1  augustss 
    555  1.23      chap 	/*
    556  1.23      chap 	 * Force every channel to the same patch if requested by the user.
    557   1.1  augustss 	 */
    558  1.11  augustss 	if (sameprogram) {
    559  1.11  augustss 		for(t = 0; t < 16; t++) {
    560  1.23      chap 			send_event(&SEQ_MK_CHN(PGM_CHANGE, .device=unit,
    561  1.23      chap 			    .channel=t, .program=sameprogram-1));
    562  1.10  augustss 		}
    563  1.10  augustss 	}
    564  1.28  christos 	/*
    565  1.23      chap 	 * Play MIDI events by selecting the track with the lowest
    566  1.23      chap 	 * delta.  Execute the event, update the delta and repeat.
    567  1.23      chap 	 *
    568  1.23      chap 	 * The ticks variable is the number of ticks that make up a beat
    569  1.23      chap 	 * (beat: 24 MIDI clocks always, a quarter note by usual convention)
    570  1.23      chap 	 * and is used as a reference value for the delays between
    571   1.1  augustss 	 * the MIDI events.
    572   1.1  augustss 	 */
    573  1.23      chap 	BuildHeap(tracks, ntrks); /* tracks[0].indirect is always next */
    574   1.1  augustss 	for (;;) {
    575  1.23      chap 		tp = tracks[0].indirect;
    576  1.23      chap 		if ((verbose > 2 && tp->delta > 0)  ||  verbose > 3) {
    577  1.25        he 			printf("DELAY %4ld TRACK %2td%s",
    578  1.25        he 			       tp->delta, tp - tracks, verbose>3?" ":"\n");
    579   1.1  augustss 			fflush(stdout);
    580   1.1  augustss 		}
    581  1.23      chap 		if (tp->delta > 0) {
    582  1.23      chap 			if (!tempo_set) {
    583  1.23      chap 				if (verbose || showmeta)
    584  1.23      chap 					printf("No initial tempo;"
    585  1.23      chap 					       " defaulting:\n");
    586  1.23      chap 				dometa(META_SET_TEMPO, (u_char[]){
    587  1.23      chap 				    BASETEMPO >> 16,
    588  1.23      chap 				    (BASETEMPO >> 8) & 0xff,
    589  1.23      chap 				    BASETEMPO & 0xff},
    590  1.23      chap 				    3);
    591   1.1  augustss 			}
    592  1.23      chap 			send_event(&SEQ_MK_TIMING(WAIT_REL,
    593  1.23      chap 			    .divisions=tp->delta));
    594   1.1  augustss 		}
    595   1.1  augustss 		byte = *tp->start++;
    596   1.1  augustss 		if (byte == MIDI_META) {
    597   1.1  augustss 			meta = *tp->start++;
    598  1.23      chap 			mlen = getlen(tp);
    599  1.23      chap 			if (verbose > 3)
    600   1.1  augustss 				printf("META %02x (%d)\n", meta, mlen);
    601   1.1  augustss 			dometa(meta, tp->start, mlen);
    602   1.1  augustss 			tp->start += mlen;
    603   1.1  augustss 		} else {
    604   1.1  augustss 			if (MIDI_IS_STATUS(byte))
    605   1.1  augustss 				tp->status = byte;
    606   1.4  augustss 			else
    607   1.4  augustss 				tp->start--;
    608   1.1  augustss 			mlen = MIDI_LENGTH(tp->status);
    609   1.3  augustss 			msg = tp->start;
    610  1.23      chap 			if (verbose > 3) {
    611   1.3  augustss 			    if (mlen == 1)
    612   1.3  augustss 				printf("MIDI %02x (%d) %02x\n",
    613   1.3  augustss 				       tp->status, mlen, msg[0]);
    614  1.28  christos 			    else
    615   1.3  augustss 				printf("MIDI %02x (%d) %02x %02x\n",
    616   1.3  augustss 				       tp->status, mlen, msg[0], msg[1]);
    617   1.3  augustss 			}
    618  1.23      chap 			if (insysex && tp->status != MIDI_SYSEX_END) {
    619  1.23      chap 				warnx("incomplete system exclusive message"
    620  1.23      chap 				      " aborted");
    621  1.23      chap 				svsysex = insysex = 0;
    622  1.23      chap 			}
    623   1.1  augustss 			status = MIDI_GET_STATUS(tp->status);
    624   1.1  augustss 			chan = MIDI_GET_CHAN(tp->status);
    625   1.1  augustss 			switch (status) {
    626   1.1  augustss 			case MIDI_NOTEOFF:
    627  1.23      chap 				send_event(&SEQ_MK_CHN(NOTEOFF, .device=unit,
    628  1.23      chap 				.channel=chan, .key=msg[0], .velocity=msg[1]));
    629  1.23      chap 				break;
    630   1.1  augustss 			case MIDI_NOTEON:
    631  1.23      chap 				send_event(&SEQ_MK_CHN(NOTEON, .device=unit,
    632  1.23      chap 				.channel=chan, .key=msg[0], .velocity=msg[1]));
    633  1.23      chap 				break;
    634   1.1  augustss 			case MIDI_KEY_PRESSURE:
    635  1.23      chap 				send_event(&SEQ_MK_CHN(KEY_PRESSURE,
    636  1.23      chap 				.device=unit, .channel=chan,
    637  1.23      chap 				.key=msg[0], .pressure=msg[1]));
    638   1.1  augustss 				break;
    639   1.1  augustss 			case MIDI_CTL_CHANGE:
    640  1.23      chap 				send_event(&SEQ_MK_CHN(CTL_CHANGE,
    641  1.23      chap 				.device=unit, .channel=chan,
    642  1.23      chap 				.controller=msg[0], .value=msg[1]));
    643   1.1  augustss 				break;
    644   1.1  augustss 			case MIDI_PGM_CHANGE:
    645  1.23      chap 				if (!sameprogram)
    646  1.23      chap 					send_event(&SEQ_MK_CHN(PGM_CHANGE,
    647  1.23      chap 					.device=unit, .channel=chan,
    648  1.23      chap 					.program=msg[0]));
    649  1.23      chap 				break;
    650   1.1  augustss 			case MIDI_CHN_PRESSURE:
    651  1.23      chap 				send_event(&SEQ_MK_CHN(CHN_PRESSURE,
    652  1.23      chap 				.device=unit, .channel=chan, .pressure=msg[0]));
    653   1.1  augustss 				break;
    654   1.1  augustss 			case MIDI_PITCH_BEND:
    655  1.23      chap 				send_event(&SEQ_MK_CHN(PITCH_BEND,
    656  1.23      chap 				.device=unit, .channel=chan,
    657  1.23      chap 				.value=(msg[0] & 0x7f) | ((msg[1] & 0x7f)<<7)));
    658   1.1  augustss 				break;
    659   1.6  augustss 			case MIDI_SYSTEM_PREFIX:
    660  1.23      chap 				mlen = getlen(tp);
    661  1.23      chap 				if (tp->status == MIDI_SYSEX_START) {
    662   1.6  augustss 					send_sysex(tp->start, mlen);
    663  1.23      chap 					break;
    664  1.23      chap 				} else if (tp->status == MIDI_SYSEX_END) {
    665  1.23      chap 				/* SMF uses SYSEX_END as CONTINUATION/ESCAPE */
    666  1.23      chap 					if (insysex) { /* CONTINUATION */
    667  1.23      chap 						send_sysex(tp->start, mlen);
    668  1.23      chap 					} else { /* ESCAPE */
    669  1.28  christos 						for (; mlen > 0 ; -- mlen) {
    670  1.23      chap 							send_event(
    671  1.23      chap 							    &SEQ_MK_EVENT(putc,
    672  1.23      chap 							    SEQOLD_MIDIPUTC,
    673  1.23      chap 							    .device=unit,
    674  1.23      chap 							    .byte=*(tp->start++)
    675  1.28  christos 							   ));
    676  1.23      chap 						}
    677  1.23      chap 					}
    678  1.23      chap 					break;
    679  1.23      chap 				}
    680  1.33       mrg 				/* Sorry, can't do this yet */
    681  1.33       mrg 				/* FALLTHROUGH */
    682   1.3  augustss 			default:
    683   1.3  augustss 				if (verbose)
    684   1.3  augustss 					printf("MIDI event 0x%02x ignored\n",
    685   1.3  augustss 					       tp->status);
    686   1.1  augustss 			}
    687   1.1  augustss 			tp->start += mlen;
    688   1.1  augustss 		}
    689  1.23      chap 		if (tp->start >= tp->end) {
    690  1.23      chap 			ntrks = ShrinkHeap(tracks, ntrks); /* track gone */
    691  1.23      chap 			if (0 == ntrks)
    692  1.23      chap 				break;
    693  1.23      chap 		} else
    694  1.23      chap 			tp->delta = getvar(tp);
    695  1.23      chap 		Heapify(tracks, ntrks, 0);
    696   1.1  augustss 	}
    697  1.30       mrg 	if (play && ioctl(fd, SEQUENCER_SYNC, 0) < 0)
    698   1.6  augustss 		err(1, "SEQUENCER_SYNC");
    699   1.1  augustss 
    700   1.6  augustss  ret:
    701   1.1  augustss 	free(tracks);
    702   1.1  augustss }
    703   1.1  augustss 
    704  1.29  jmcneill static int
    705  1.29  jmcneill parse_unit(const char *sunit)
    706  1.29  jmcneill {
    707  1.29  jmcneill 	const char *osunit = sunit;
    708  1.29  jmcneill 	long n;
    709  1.29  jmcneill 	char *ep;
    710  1.29  jmcneill 
    711  1.29  jmcneill 	if (strncmp(sunit, "midi", strlen("midi")) == 0)
    712  1.29  jmcneill 		sunit += strlen("midi");
    713  1.29  jmcneill 
    714  1.29  jmcneill 	errno = 0;
    715  1.29  jmcneill 	n = strtol(sunit, &ep, 10);
    716  1.29  jmcneill 	if (n < 0 || n > INT_MAX || *ep != '\0' ||
    717  1.29  jmcneill 	    (errno == ERANGE &&
    718  1.29  jmcneill 	    (n == LONG_MAX || n == LONG_MIN)))
    719  1.29  jmcneill 		errx(1, "bad midi unit -- %s", osunit);
    720  1.29  jmcneill 
    721  1.29  jmcneill 	return (int)n;
    722  1.29  jmcneill }
    723  1.29  jmcneill 
    724   1.1  augustss int
    725  1.16  augustss main(int argc, char **argv)
    726   1.1  augustss {
    727   1.1  augustss 	int ch;
    728   1.1  augustss 	int listdevs = 0;
    729   1.1  augustss 	int example = 0;
    730  1.31       mrg 	int silence = 0;
    731   1.1  augustss 	int nmidi;
    732  1.16  augustss 	const char *file = DEVMUSIC;
    733  1.16  augustss 	const char *sunit;
    734   1.1  augustss 	struct synth_info info;
    735   1.1  augustss 	FILE *f;
    736  1.16  augustss 
    737  1.16  augustss 	if ((sunit = getenv("MIDIUNIT")))
    738  1.29  jmcneill 		unit = parse_unit(sunit);
    739   1.1  augustss 
    740  1.31       mrg 	while ((ch = getopt(argc, argv, "?d:f:lmp:qst:vx")) != -1) {
    741   1.1  augustss 		switch(ch) {
    742   1.1  augustss 		case 'd':
    743  1.29  jmcneill 			unit = parse_unit(optarg);
    744   1.1  augustss 			break;
    745   1.1  augustss 		case 'f':
    746   1.1  augustss 			file = optarg;
    747   1.1  augustss 			break;
    748   1.1  augustss 		case 'l':
    749   1.1  augustss 			listdevs++;
    750   1.1  augustss 			break;
    751   1.1  augustss 		case 'm':
    752   1.1  augustss 			showmeta++;
    753  1.10  augustss 			break;
    754  1.10  augustss 		case 'p':
    755  1.10  augustss 			sameprogram = atoi(optarg);
    756   1.3  augustss 			break;
    757   1.3  augustss 		case 'q':
    758   1.3  augustss 			play = 0;
    759   1.1  augustss 			break;
    760  1.31       mrg 		case 's':
    761  1.31       mrg 			silence++;
    762  1.31       mrg 			break;
    763   1.1  augustss 		case 't':
    764   1.2  augustss 			ttempo = atoi(optarg);
    765   1.1  augustss 			break;
    766   1.1  augustss 		case 'v':
    767   1.1  augustss 			verbose++;
    768   1.1  augustss 			break;
    769   1.1  augustss 		case 'x':
    770   1.1  augustss 			example++;
    771   1.1  augustss 			break;
    772   1.1  augustss 		case '?':
    773   1.1  augustss 		default:
    774   1.1  augustss 			usage();
    775   1.1  augustss 		}
    776   1.1  augustss 	}
    777   1.1  augustss 	argc -= optind;
    778   1.1  augustss 	argv += optind;
    779  1.28  christos 
    780  1.15  augustss 	if (!play)
    781  1.15  augustss 		goto output;
    782  1.15  augustss 
    783   1.1  augustss 	fd = open(file, O_WRONLY);
    784   1.1  augustss 	if (fd < 0)
    785   1.1  augustss 		err(1, "%s", file);
    786   1.1  augustss 	if (ioctl(fd, SEQUENCER_NRMIDIS, &nmidi) < 0)
    787   1.1  augustss 		err(1, "ioctl(SEQUENCER_NRMIDIS) failed, ");
    788   1.1  augustss 	if (nmidi == 0)
    789  1.17    itojun 		errx(1, "Sorry, no MIDI devices available");
    790   1.1  augustss 	if (listdevs) {
    791   1.1  augustss 		for (info.device = 0; info.device < nmidi; info.device++) {
    792   1.1  augustss 			if (ioctl(fd, SEQUENCER_INFO, &info) < 0)
    793   1.1  augustss 				err(1, "ioctl(SEQUENCER_INFO) failed, ");
    794   1.1  augustss 			printf("%d: %s\n", info.device, info.name);
    795   1.1  augustss 		}
    796   1.1  augustss 		exit(0);
    797   1.1  augustss 	}
    798   1.9  augustss 
    799  1.15  augustss  output:
    800   1.1  augustss 	if (example)
    801   1.9  augustss 		while (example--)
    802   1.9  augustss 			playdata(sample, sizeof sample, "<Gubben Noa>");
    803  1.31       mrg 	else if (silence)
    804  1.31       mrg 		while (silence--)
    805  1.31       mrg 			playdata(silence_sample, sizeof silence_sample,
    806  1.31       mrg 				 "<Silence>");
    807   1.1  augustss 	else if (argc == 0)
    808   1.1  augustss 		playfile(stdin, "<stdin>");
    809   1.1  augustss 	else
    810   1.1  augustss 		while (argc--) {
    811   1.1  augustss 			f = fopen(*argv, "r");
    812   1.1  augustss 			if (f == NULL)
    813   1.1  augustss 				err(1, "%s", *argv);
    814   1.1  augustss 			else {
    815   1.1  augustss 				playfile(f, *argv);
    816   1.1  augustss 				fclose(f);
    817   1.1  augustss 			}
    818   1.1  augustss 			argv++;
    819   1.1  augustss 		}
    820   1.1  augustss 
    821   1.1  augustss 	exit(0);
    822   1.1  augustss }
    823  1.23      chap 
    824  1.23      chap /*
    825  1.23      chap  * relative-time priority queue (min-heap). Properties:
    826  1.23      chap  * 1. The delta time at a node is relative to the node's parent's time.
    827  1.23      chap  * 2. When an event is dequeued from a track, the delta time of the new head
    828  1.23      chap  *    event is relative to the time of the event just dequeued.
    829  1.23      chap  * Therefore:
    830  1.23      chap  * 3. After dequeueing the head event from the track at heap root, the next
    831  1.23      chap  *    event's time is directly comparable to the root's children.
    832  1.23      chap  * These properties allow the heap to be maintained with delta times throughout.
    833  1.23      chap  * Insert is also implementable, but not needed: all the tracks are present
    834  1.23      chap  * at first; they just go away as they end.
    835  1.23      chap  */
    836  1.23      chap 
    837  1.28  christos #define PARENT(i) ((i - 1) >> 1)
    838  1.28  christos #define LEFT(i)   ((i << 1) + 1)
    839  1.28  christos #define RIGHT(i)  ((i + 1) << 1)
    840  1.23      chap #define DTIME(i)  (t[i].indirect->delta)
    841  1.28  christos #define SWAP(i, j) do { \
    842  1.23      chap     struct track *_t = t[i].indirect; \
    843  1.23      chap     t[i].indirect = t[j].indirect; \
    844  1.23      chap     t[j].indirect = _t; \
    845  1.34    rillig } while (0)
    846  1.23      chap 
    847  1.28  christos static void
    848  1.23      chap Heapify(struct track *t, int ntrks, int node)
    849  1.23      chap {
    850  1.23      chap 	int lc, rc, mn;
    851  1.28  christos 
    852  1.23      chap 	lc = LEFT(node);
    853  1.23      chap 	rc = RIGHT(node);
    854  1.28  christos 
    855  1.23      chap 	if (rc >= ntrks) {			/* no right child */
    856  1.23      chap 		if (lc >= ntrks)		/* node is a leaf */
    857  1.23      chap 			return;
    858  1.23      chap 		if (DTIME(node) > DTIME(lc))
    859  1.28  christos 			SWAP(node, lc);
    860  1.23      chap 		DTIME(lc) -= DTIME(node);
    861  1.23      chap 		return;				/* no rc ==> lc is a leaf */
    862  1.23      chap 	}
    863  1.23      chap 
    864  1.23      chap 	mn = lc;
    865  1.23      chap 	if (DTIME(lc) > DTIME(rc))
    866  1.23      chap 		mn = rc;
    867  1.23      chap 	if (DTIME(node) <= DTIME(mn)) {
    868  1.23      chap 		DTIME(rc) -= DTIME(node);
    869  1.23      chap 		DTIME(lc) -= DTIME(node);
    870  1.23      chap 		return;
    871  1.23      chap 	}
    872  1.28  christos 
    873  1.28  christos 	SWAP(node, mn);
    874  1.23      chap 	DTIME(rc) -= DTIME(node);
    875  1.23      chap 	DTIME(lc) -= DTIME(node);
    876  1.23      chap 	Heapify(t, ntrks, mn); /* gcc groks tail recursion */
    877  1.23      chap }
    878  1.23      chap 
    879  1.28  christos static void
    880  1.23      chap BuildHeap(struct track *t, int ntrks)
    881  1.23      chap {
    882  1.23      chap 	int node;
    883  1.28  christos 
    884  1.28  christos 	for (node = PARENT(ntrks - 1); node --> 0;)
    885  1.23      chap 		Heapify(t, ntrks, node);
    886  1.23      chap }
    887  1.23      chap 
    888  1.23      chap /*
    889  1.23      chap  * Make the heap 1 item smaller by discarding the track at the root. Move the
    890  1.23      chap  * rightmost bottom-level leaf to the root and decrement ntrks. It remains to
    891  1.23      chap  * run Heapify, which the caller is expected to do. Returns the new ntrks.
    892  1.23      chap  */
    893  1.28  christos static int
    894  1.23      chap ShrinkHeap(struct track *t, int ntrks)
    895  1.23      chap {
    896  1.23      chap 	int ancest;
    897  1.28  christos 
    898  1.28  christos 	--ntrks;
    899  1.28  christos 	for (ancest = PARENT(ntrks); ancest > 0; ancest = PARENT(ancest))
    900  1.23      chap 		DTIME(ntrks) += DTIME(ancest);
    901  1.23      chap 	t[0].indirect = t[ntrks].indirect;
    902  1.23      chap 	return ntrks;
    903  1.23      chap }
    904