aboutsummaryrefslogtreecommitdiff
path: root/lib/libc/include/generic-netbsd/sys/midiio.h
blob: 8653b880bb989be24bc9a76fc8b8695fb3fd8564 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
/*	$NetBSD: midiio.h,v 1.16 2015/09/06 06:01:02 dholland Exp $	*/

/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Lennart Augustsson (augustss@NetBSD.org) and (native API structures
 * and macros) Chapman Flack (chap@NetBSD.org).
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _SYS_MIDIIO_H_
#define _SYS_MIDIIO_H_

/*
 * The API defined here produces events compatible with the OSS MIDI API at
 * the binary level.
 */

#include <machine/endian_machdep.h>
#include <sys/ioccom.h>

/*
 * ioctl() commands for /dev/midi##
 * XXX is directly frobbing an MPU401 even supported? isn't it just run
 * in UART mode?
 */
typedef struct {
	unsigned	char cmd;
	char		nr_args, nr_returns;
	unsigned char	data[30];
} mpu_command_rec;

#define MIDI_PRETIME		_IOWR('m', 0, int)
#define MIDI_MPUMODE		_IOWR('m', 1, int)
#define MIDI_MPUCMD		_IOWR('m', 2, mpu_command_rec)


/* The MPU401 command acknowledge and active sense command */
#define MIDI_ACK	0xfe


/* Sequencer */
#define SEQUENCER_RESET			_IO  ('Q', 0)
#define SEQUENCER_SYNC			_IO  ('Q', 1)
#define SEQUENCER_INFO			_IOWR('Q', 2, struct synth_info)
#define SEQUENCER_CTRLRATE		_IOWR('Q', 3, int)
#define SEQUENCER_GETOUTCOUNT		_IOR ('Q', 4, int)
#define SEQUENCER_GETINCOUNT		_IOR ('Q', 5, int)
/*#define SEQUENCER_PERCMODE		_IOW ('Q', 6, int)*/
/*#define SEQUENCER_TESTMIDI		_IOW ('Q', 8, int)*/
#define SEQUENCER_RESETSAMPLES		_IOW ('Q', 9, int)
/*
 * The sequencer at present makes no distinction between a 'synth' and a 'midi'.
 * This is actually a cleaner layering than OSS: devices that are onboard
 * synths just attach midi(4) via midisyn and present an ordinary MIDI face to
 * the system. At present the same number is returned for NRSYNTHS and NRMIDIS
 * but don't believe both, or you'll think you have twice as many devices as
 * you really have. The MIDI_INFO ioctl isn't implemented; use SEQUENCER_INFO
 * (which corresponds to OSS's SYNTH_INFO) to get information on any kind of
 * device, though the struct synth_info it uses has some members that only
 * pertain to synths (and get filled in with fixed, probably wrong values,
 * anyway).
 */
#define SEQUENCER_NRSYNTHS		_IOR ('Q',10, int)
#define SEQUENCER_NRMIDIS		_IOR ('Q',11, int)
/*#define SEQUENCER_MIDI_INFO		_IOWR('Q',12, struct midi_info)*/
#define SEQUENCER_THRESHOLD		_IOW ('Q',13, int)
#define SEQUENCER_MEMAVL		_IOWR('Q',14, int)
/*#define SEQUENCER_FM_4OP_ENABLE		_IOW ('Q',15, int)*/
#define SEQUENCER_PANIC			_IO  ('Q',17)
#define SEQUENCER_OUTOFBAND		_IOW ('Q',18, struct seq_event_rec)
#define SEQUENCER_GETTIME		_IOR ('Q',19, int)
/*#define SEQUENCER_ID			_IOWR('Q',20, struct synth_info)*/
/*#define SEQUENCER_CONTROL		_IOWR('Q',21, struct synth_control)*/
/*#define SEQUENCER_REMOVESAMPLE		_IOWR('Q',22, struct remove_sample)*/

#if 0
typedef struct synth_control {
	int	devno;		/* Synthesizer # */
	char	data[4000];	/* Device specific command/data record */
} synth_control;

typedef struct remove_sample {
	int	devno;		/* Synthesizer # */
	int	bankno;		/* MIDI bank # (0=General MIDI) */
	int	instrno;	/* MIDI instrument number */
} remove_sample;
#endif

#define CMDSIZE 8
typedef struct seq_event_rec {
	u_char	arr[CMDSIZE];
} seq_event_rec;

struct synth_info {
	char	name[30];
	int	device;
	int	synth_type;
#define SYNTH_TYPE_FM			0
#define SYNTH_TYPE_SAMPLE		1
#define SYNTH_TYPE_MIDI			2

	int	synth_subtype;
#define SYNTH_SUB_FM_TYPE_ADLIB		0x00
#define SYNTH_SUB_FM_TYPE_OPL3		0x01
#define SYNTH_SUB_MIDI_TYPE_MPU401	0x401

#define SYNTH_SUB_SAMPLE_TYPE_BASIC	0x10
#define SYNTH_SUB_SAMPLE_TYPE_GUS	SAMPLE_TYPE_BASIC

	int	nr_voices;
	int	instr_bank_size;
	u_int	capabilities;
#define SYNTH_CAP_OPL3			0x00000002
#define SYNTH_CAP_INPUT			0x00000004
};

/* Sequencer timer */
#define SEQUENCER_TMR_TIMEBASE		_IOWR('T', 1, int)
#define SEQUENCER_TMR_START		_IO  ('T', 2)
#define SEQUENCER_TMR_STOP		_IO  ('T', 3)
#define SEQUENCER_TMR_CONTINUE		_IO  ('T', 4)
#define SEQUENCER_TMR_TEMPO		_IOWR('T', 5, int)
#define SEQUENCER_TMR_SOURCE		_IOWR('T', 6, int)
#  define SEQUENCER_TMR_INTERNAL	0x00000001
#if 0
#  define SEQUENCER_TMR_EXTERNAL	0x00000002
#  define SEQUENCER_TMR_MODE_MIDI	0x00000010
#  define SEQUENCER_TMR_MODE_FSK	0x00000020
#  define SEQUENCER_TMR_MODE_CLS	0x00000040
#  define SEQUENCER_TMR_MODE_SMPTE	0x00000080
#endif
#define SEQUENCER_TMR_METRONOME		_IOW ('T', 7, int)
#define SEQUENCER_TMR_SELECT		_IOW ('T', 8, int)


#define MIDI_CTRL_BANK_SELECT_MSB	0
#define MIDI_CTRL_MODULATION_MSB	1
#define MIDI_CTRL_BREATH_MSB		2
#define MIDI_CTRL_FOOT_MSB		4
#define MIDI_CTRL_PORTAMENTO_TIME_MSB	5
#define MIDI_CTRL_DATA_ENTRY_MSB	6
#define MIDI_CTRL_CHANNEL_VOLUME_MSB	7
#define MIDI_CTRL_BALANCE_MSB		8
#define MIDI_CTRL_PAN_MSB		10
#define MIDI_CTRL_EXPRESSION_MSB	11
#define MIDI_CTRL_EFFECT_1_MSB		12
#define MIDI_CTRL_EFFECT_2_MSB		13
#define MIDI_CTRL_GENERAL_PURPOSE_1_MSB	16
#define MIDI_CTRL_GENERAL_PURPOSE_2_MSB	17
#define MIDI_CTRL_GENERAL_PURPOSE_3_MSB	18
#define MIDI_CTRL_GENERAL_PURPOSE_4_MSB	19
#define MIDI_CTRL_BANK_SELECT_LSB	32
#define MIDI_CTRL_MODULATION_LSB	33
#define MIDI_CTRL_BREATH_LSB		34
#define MIDI_CTRL_FOOT_LSB		36
#define MIDI_CTRL_PORTAMENTO_TIME_LSB	37
#define MIDI_CTRL_DATA_ENTRY_LSB	38
#define MIDI_CTRL_CHANNEL_VOLUME_LSB	39
#define MIDI_CTRL_BALANCE_LSB		40
#define MIDI_CTRL_PAN_LSB		42
#define MIDI_CTRL_EXPRESSION_LSB	43
#define MIDI_CTRL_EFFECT_1_LSB		44
#define MIDI_CTRL_EFFECT_2_LSB		45
#define MIDI_CTRL_GENERAL_PURPOSE_1_LSB	48
#define MIDI_CTRL_GENERAL_PURPOSE_2_LSB	49
#define MIDI_CTRL_GENERAL_PURPOSE_3_LSB	50
#define MIDI_CTRL_GENERAL_PURPOSE_4_LSB	51
#define MIDI_CTRL_HOLD_1		64
#define MIDI_CTRL_PORTAMENTO		65
#define MIDI_CTRL_SOSTENUTO		66
#define MIDI_CTRL_SOFT_PEDAL		67
#define MIDI_CTRL_LEGATO		68
#define MIDI_CTRL_HOLD_2		69
#define MIDI_CTRL_SOUND_VARIATION	70
#define MIDI_CTRL_HARMONIC_INTENSITY	71
#define MIDI_CTRL_RELEASE_TIME		72
#define MIDI_CTRL_ATTACK_TIME		73
#define MIDI_CTRL_BRIGHTNESS		74
#define MIDI_CTRL_DECAY_TIME		75
#define MIDI_CTRL_VIBRATO_RATE		76
#define MIDI_CTRL_VIBRATO_DEPTH		77
#define MIDI_CTRL_VIBRATO_DELAY		78
#define MIDI_CTRL_VIBRATO_DECAY		MIDI_CTRL_VIBRATO_DELAY /*deprecated*/
#define MIDI_CTRL_SOUND_10		79
#define MIDI_CTRL_GENERAL_PURPOSE_5	80
#define MIDI_CTRL_GENERAL_PURPOSE_6	81
#define MIDI_CTRL_GENERAL_PURPOSE_7	82
#define MIDI_CTRL_GENERAL_PURPOSE_8	83
#define MIDI_CTRL_PORTAMENTO_CONTROL	84
#define MIDI_CTRL_EFFECT_DEPTH_1	91
#define MIDI_CTRL_EFFECT_DEPTH_2	92
#define MIDI_CTRL_EFFECT_DEPTH_3	93
#define MIDI_CTRL_EFFECT_DEPTH_4	94
#define MIDI_CTRL_EFFECT_DEPTH_5	95
#define MIDI_CTRL_RPN_INCREMENT		96
#define MIDI_CTRL_RPN_DECREMENT		97
#define MIDI_CTRL_NRPN_LSB		98
#define MIDI_CTRL_NRPN_MSB		99
#define MIDI_CTRL_RPN_LSB		100
#define MIDI_CTRL_RPN_MSB		101
#define MIDI_CTRL_SOUND_OFF		120
#define MIDI_CTRL_RESET			121
#define MIDI_CTRL_LOCAL			122
#define MIDI_CTRL_NOTES_OFF		123
#define MIDI_CTRL_ALLOFF		MIDI_CTRL_NOTES_OFF /*deprecated*/
#define MIDI_CTRL_OMNI_OFF		124
#define MIDI_CTRL_OMNI_ON		125
#define MIDI_CTRL_POLY_OFF		126
#define MIDI_CTRL_POLY_ON		127

#define MIDI_BEND_NEUTRAL	(1<<13)

#define MIDI_RPN_PITCH_BEND_SENSITIVITY	0
#define MIDI_RPN_CHANNEL_FINE_TUNING	1
#define MIDI_RPN_CHANNEL_COARSE_TUNING	2
#define MIDI_RPN_TUNING_PROGRAM_CHANGE	3
#define MIDI_RPN_TUNING_BANK_SELECT	4
#define MIDI_RPN_MODULATION_DEPTH_RANGE	5

#define MIDI_NOTEOFF		0x80
#define MIDI_NOTEON		0x90
#define MIDI_KEY_PRESSURE	0xA0
#define MIDI_CTL_CHANGE		0xB0
#define MIDI_PGM_CHANGE		0xC0
#define MIDI_CHN_PRESSURE	0xD0
#define MIDI_PITCH_BEND		0xE0
#define MIDI_SYSTEM_PREFIX	0xF0

#define MIDI_IS_STATUS(d) ((d) >= 0x80)
#define MIDI_IS_COMMON(d) ((d) >= 0xf0)

#define MIDI_SYSEX_START	0xF0
#define MIDI_SYSEX_END		0xF7

#define MIDI_GET_STATUS(d) ((d) & 0xf0)
#define MIDI_GET_CHAN(d) ((d) & 0x0f)

#define MIDI_HALF_VEL 64

#define SEQ_LOCAL		0x80
#define SEQ_TIMING		0x81
#define SEQ_CHN_COMMON		0x92
#define SEQ_CHN_VOICE		0x93
#define SEQ_SYSEX		0x94
#define SEQ_FULLSIZE		0xfd

#define SEQ_MK_CHN_VOICE(e, unit, cmd, chan, key, vel) (\
    (e)->arr[0] = SEQ_CHN_VOICE, (e)->arr[1] = (unit), (e)->arr[2] = (cmd),\
    (e)->arr[3] = (chan), (e)->arr[4] = (key), (e)->arr[5] = (vel),\
    (e)->arr[6] = 0, (e)->arr[7] = 0)
#define SEQ_MK_CHN_COMMON(e, unit, cmd, chan, p1, p2, w14) (\
    (e)->arr[0] = SEQ_CHN_COMMON, (e)->arr[1] = (unit), (e)->arr[2] = (cmd),\
    (e)->arr[3] = (chan), (e)->arr[4] = (p1), (e)->arr[5] = (p2),\
    *(short*)&(e)->arr[6] = (w14))

#if _BYTE_ORDER == _BIG_ENDIAN
/* big endian */
#define SEQ_PATCHKEY(id) (0xfd00|id)
#else
/* little endian */
#define SEQ_PATCHKEY(id) ((id<<8)|0xfd)
#endif
struct sysex_info {
	uint16_t	key;	/* Use SYSEX_PATCH or MAUI_PATCH here */
#define SEQ_SYSEX_PATCH	SEQ_PATCHKEY(0x05)
#define SEQ_MAUI_PATCH	SEQ_PATCHKEY(0x06)
	int16_t	device_no;	/* Synthesizer number */
	int32_t	len;		/* Size of the sysex data in bytes */
	u_char	data[1];	/* Sysex data starts here */
};
#define SEQ_SYSEX_HDRSIZE ((u_long)((struct sysex_info *)0)->data)

typedef unsigned char sbi_instr_data[32];
struct sbi_instrument {
	uint16_t key;	/* FM_PATCH or OPL3_PATCH */
#define SBI_FM_PATCH	SEQ_PATCHKEY(0x01)
#define SBI_OPL3_PATCH	SEQ_PATCHKEY(0x03)
	int16_t		device;
	int32_t		channel;
	sbi_instr_data	operators;
};

#define TMR_RESET		0	/* beware: not an OSS event */
#define TMR_WAIT_REL		1	/* Time relative to the prev time */
#define TMR_WAIT_ABS		2	/* Absolute time since TMR_START */
#define TMR_STOP		3
#define TMR_START		4
#define TMR_CONTINUE		5
#define TMR_TEMPO		6
#define TMR_ECHO		8
#define TMR_CLOCK		9	/* MIDI clock */
#define TMR_SPP			10	/* Song position pointer */
#define TMR_TIMESIG		11	/* Time signature */

/* Old sequencer definitions */
#define SEQOLD_CMDSIZE 4

#define SEQOLD_NOTEOFF		0
#define SEQOLD_NOTEON		1
#define SEQOLD_WAIT		TMR_WAIT_ABS
#define SEQOLD_PGMCHANGE	3
#define SEQOLD_SYNCTIMER	TMR_START
#define SEQOLD_MIDIPUTC		5
#define SEQOLD_ECHO		TMR_ECHO
#define SEQOLD_AFTERTOUCH	9
#define SEQOLD_CONTROLLER	10
#define SEQOLD_PRIVATE		0xfe
#define SEQOLD_EXTENDED		0xff

/*
 * The 'midipitch' data type, used in the kernel between the midisyn layer and
 * onboard synth drivers, and in userland as parameters to the MIDI Tuning Spec
 * (RP-012) universal-system-exclusive messages. It is a MIDI key number shifted
 * left to accommodate 14 bit sub-semitone resolution. In this representation,
 * tuning and bending adjustments are simple addition and subtraction.
 */
typedef int32_t midipitch_t;

/*
 * Nominal conversions between midipitches and key numbers. (Beware that these
 * are the nominal, standard correspondences, but whole point of the MIDI Tuning
 * Spec is that you can set things up so the hardware might render key N at
 * actual pitch MIDIPITCH_FROM_KEY(N)+c for some correction c.)
 */
#define MIDIPITCH_FROM_KEY(k) ((k)<<14)
#define MIDIPITCH_TO_KEY(mp) (((mp)+(1<<13))>>14)

#define MIDIPITCH_MAX (MIDIPITCH_FROM_KEY(128)-2) /* ...(128)-1 is reserved */
#define MIDIPITCH_OCTAVE  196608
#define MIDIPITCH_SEMITONE 16384
#define MIDIPITCH_CENT       164 /* this, regrettably, is inexact. */

/*
 * For rendering, convert a midipitch (after all tuning adjustments) to Hz.
 * The conversion is DEFINED as MIDI key 69.00000 (A) === 440 Hz equal tempered
 * always. Alternate tunings are obtained by adjusting midipitches.
 *
 * The midihz18_t (Hz shifted left for 18-bit sub-Hz resolution) covers the
 * full midipitch range without losing 21-bit precision, as the lowest midipitch
 * is ~8 Hz (~3 bits left of radix point, 18 right) and for the highest the
 * result still fits in a uint32.
 */
typedef uint32_t midihz18_t;

#define MIDIHZ18_TO_HZ(h18) ((h18)>>18) /* truncates! ok for dbg msgs maybe */

#ifndef _KERNEL
/*
 * With floating point in userland, can also manipulate midipitches as
 * floating-point fractional MIDI key numbers (tuning adjustments are still
 * additive), and hz18 as fractional Hz (adjustments don't add in this form).
 */
#include <math.h>
#define MIDIPITCH_TO_FRKEY(mp) (scalbn((mp),-14))
#define MIDIPITCH_FROM_FRKEY(frk) ((midipitch_t)round(scalbn((frk),14)))
#define MIDIHZ18_TO_FRHZ(h18) (scalbn((h18),-18))
#define MIDIHZ18_FROM_FRHZ(frh) ((midihz18_t)round(scalbn((frh),18)))

#define MIDIPITCH_TO_FRHZ(mp) (440*pow(2,(MIDIPITCH_TO_FRKEY((mp))-69)/12))
#define MIDIPITCH_FROM_FRHZ(fhz) \
                               MIDIPITCH_FROM_FRKEY(69+12*log((fhz)/440)/log(2))
#define MIDIPITCH_TO_HZ18(mp) MIDIHZ18_FROM_FRHZ(MIDIPITCH_TO_FRHZ((mp)))
#define MIDIPITCH_FROM_HZ18(h18) MIDIPITCH_FROM_FRHZ(MIDIHZ18_TO_FRHZ((h18)))

#else /* no fp in kernel; only an accurate to-hz18 conversion is implemented */

extern midihz18_t midisyn_mp2hz18(midipitch_t);
#define MIDIPITCH_TO_HZ18(mp) (midisyn_mp2hz18((mp)))

#endif /* _KERNEL */


/*
 * A native API for the /dev/music sequencer device follows. The event
 * structures are OSS events at the level of bytes, but for developing or
 * porting applications some macros and documentation are needed to generate
 * and dissect the events; here they are. For porting existing OSS applications,
 * sys/soundcard.h can be extended to supply the usual OSS macros, defining them
 * in terms of these.
 */

/*
 * TODO: determine OSS compatible structures for TMR_RESET and TMR_CLOCK,
 *       OSS values of EV_SYSTEM, SNDCTL_SEQ_ACTSENSE_ENABLE,
 *       SNDCTL_SEQ_TIMING_ENABLE, and SNDCTL_SEQ_RT_ENABLE.
 * (TMR_RESET may be a NetBSD extension: it is generated in sequencer.c and
 * has no args. To be corrected if a different definition is found anywhere.)
 */
typedef union {

#define _EVT_HDR \
	uint8_t tag

	_EVT_HDR;

#define _LOCAL_HDR \
	_EVT_HDR; \
	uint8_t op

	struct { _LOCAL_HDR; } local;

	struct {
		_LOCAL_HDR;
		uint16_t _zero;
		uint32_t devmask;
	} l_startaudio;

/* define a constructor for local evts - someday when we support any */

#define _TIMING_HDR \
	_LOCAL_HDR; \
	uint16_t _zeroh
	struct { _TIMING_HDR; } timing;
	
	struct {
		_TIMING_HDR;
		uint32_t divisions;
	} t_WAIT_REL, t_WAIT_ABS;
	
	struct {
		_TIMING_HDR;
		uint32_t _zero;
	} t_STOP, t_START, t_CONTINUE, t_RESET;
	
	struct {
		_TIMING_HDR;
		uint32_t bpm; /* unambiguously, (MIDI clocks/minute)/24 */
	} t_TEMPO;
	
	struct {
		_TIMING_HDR;
		uint32_t cookie;
	} t_ECHO;
	
	struct {
		_TIMING_HDR;
		uint32_t midibeat; /* in low 14 bits; midibeat: 6 MIDI clocks */
	} t_SPP;
	
	struct {
		_TIMING_HDR;
#if _BYTE_ORDER == _BIG_ENDIAN
		uint8_t numerator;
		uint8_t lg2denom;
		uint8_t clks_per_click;
		uint8_t dsq_per_24clks;
#elif _BYTE_ORDER == _LITTLE_ENDIAN
		uint8_t dsq_per_24clks;
		uint8_t clks_per_click;
		uint8_t lg2denom;
		uint8_t numerator;
#else
#error "unexpected _BYTE_ORDER"
#endif
	} t_TIMESIG;
	
	struct { /* use this only to implement OSS compatibility macro */
		_TIMING_HDR;
		uint32_t signature;
	} t_osscompat_timesig;
	

#define _COMMON_HDR \
	_EVT_HDR; \
	uint8_t device; \
	uint8_t op; \
	uint8_t channel

	struct { _COMMON_HDR; } common;

	struct {
		_COMMON_HDR;
		uint8_t controller;
		uint8_t _zero;
		uint16_t value;
	} c_CTL_CHANGE;
	
	struct {
		_COMMON_HDR;
		uint8_t program;
		uint8_t _zero0;
		uint16_t _zero1;
	} c_PGM_CHANGE;
	
	struct {
		_COMMON_HDR;
		uint8_t pressure;
		uint8_t _zero0;
		uint16_t _zero1;
	} c_CHN_PRESSURE;
	
	struct {
		_COMMON_HDR;
		uint8_t _zero0;
		uint8_t _zero1;
		uint16_t value;
	} c_PITCH_BEND;

#define _VOICE_HDR \
	_COMMON_HDR; \
	uint8_t key

	struct { _VOICE_HDR; }  voice;

	struct {
		_VOICE_HDR;
		uint8_t velocity;
		uint16_t _zero;
	} c_NOTEOFF, c_NOTEON;

	struct {
		_VOICE_HDR;
		uint8_t pressure;
		uint16_t _zero;
	} c_KEY_PRESSURE;

	struct {
		_EVT_HDR;
		uint8_t device;
		uint8_t buffer[6];
	} sysex;

	struct {
		_EVT_HDR;
		uint8_t device;
		uint8_t status;
		uint8_t data[2];
	} system;
	
	struct {
		_EVT_HDR;
		uint8_t byte;
		uint8_t device;
		uint8_t _zero0;
		uint32_t _zero1;
	} putc; /* a seqold event that's still needed at times, ugly as 'tis */

	struct {
		_EVT_HDR;
		uint8_t byte[7];
	} unknown; /* for debug/display */

#undef _VOICE_HDR
#undef _COMMON_HDR
#undef _TIMING_HDR
#undef _LOCAL_HDR
#undef _EVT_HDR
	
} __packed seq_event_t;

#define _SEQ_TAG_NOTEOFF	SEQ_CHN_VOICE
#define _SEQ_TAG_NOTEON 	SEQ_CHN_VOICE
#define _SEQ_TAG_KEY_PRESSURE	SEQ_CHN_VOICE

#define _SEQ_TAG_CTL_CHANGE	SEQ_CHN_COMMON
#define _SEQ_TAG_PGM_CHANGE	SEQ_CHN_COMMON
#define _SEQ_TAG_CHN_PRESSURE	SEQ_CHN_COMMON
#define _SEQ_TAG_PITCH_BEND	SEQ_CHN_COMMON

#if __STDC_VERSION__ >= 199901L

#define SEQ_MK_EVENT(_member,_tag,...)					\
(seq_event_t){ ._member = { .tag = (_tag), __VA_ARGS__ } }

#define SEQ_MK_TIMING(_op,...)						\
SEQ_MK_EVENT(t_##_op, SEQ_TIMING, .op = TMR_##_op, __VA_ARGS__)

#define SEQ_MK_CHN(_op,...)						\
SEQ_MK_EVENT(c_##_op, _SEQ_TAG_##_op, .op = MIDI_##_op, __VA_ARGS__)

#define SEQ_MK_SYSEX(_dev,...)						\
SEQ_MK_EVENT(sysex, 0x94, .device=(_dev), 				\
             .buffer={0xff, 0xff, 0xff, 0xff, 0xff, 0xff, __VA_ARGS__})

#else /* assume gcc 2.95.3 */

#define SEQ_MK_EVENT(_member,_tag,_args...)				\
(seq_event_t){ ._member = { .tag = (_tag), _args } }

#define SEQ_MK_TIMING(_op,_args...)						\
SEQ_MK_EVENT(t_##_op, SEQ_TIMING, .op = TMR_##_op, _args)

#define SEQ_MK_CHN(_op,_args...)					\
SEQ_MK_EVENT(c_##_op, _SEQ_TAG_##_op, .op = MIDI_##_op, _args)

#define SEQ_MK_SYSEX(_dev,_args...)						\
SEQ_MK_EVENT(sysex, 0x94, .device=(_dev), 				\
             .buffer={0xff, 0xff, 0xff, 0xff, 0xff, 0xff, _args})

#endif /* c99 vs. gcc 2.95.3 */

#if 0
#include <fcntl.h>
#include <stdio.h>
int
main(int argc, char **argv)
{
	int i;
	int fd;
	seq_event_t e;
	
	/* simple usage example (add a buffer to reduce syscall overhead) */
	fd = open("/dev/music", O_RDWR);
	write(fd, &SEQ_MK_TIMING(START), sizeof (seq_event_t));
	
	read(fd, &e, sizeof e);
	switch ( e.tag ) {
	case SEQ_CHN_VOICE:
		switch ( e.voice.op ) {
		case MIDI_NOTEON:
			printf("Note on, dev=%d chn=%d key=%d vel=%d\n",
			    e.c_NOTEON.device, e.c_NOTEON.channel,
			    e.c_NOTEON.key, e.c_NOTEON.velocity);
		}
	}

	/* all the macros: */
	e = SEQ_MK_TIMING(START);
	e = SEQ_MK_TIMING(STOP);
	e = SEQ_MK_TIMING(CONTINUE);
	/*
	 * Wait until the specified number of divisions from the timer start
	 * (abs) or the preceding event (rel). The number of divisions to a
	 * beat or to a MIDI clock is determined by the timebase (set by
	 * ioctl). The tempo is expressed in beats per minute, where a beat
	 * is always 24 MIDI clocks (and usually equated to a quarter note,
	 * but that can be changed with timesig)--that is, tempo is
	 * (MIDI clocks per minute)/24. The timebase is the number of divisions
	 * in a beat--that is, the number of divisions that make up 24 MIDI
	 * clocks--so the timebase is 24*(divisions per MIDI clock). The MThd
	 * header in a SMF gives the 'natural' timebase for the file; if the
	 * timebase is set accordingly, then the delay values appearing in the
	 * tracks are in terms of divisions, and can be used as WAIT_REL
	 * arguments without modification.
	 */
	e = SEQ_MK_TIMING(WAIT_ABS, .divisions=192);
	e = SEQ_MK_TIMING(WAIT_REL, .divisions=192);
	/*
	 * The 'beat' in bpm is 24 MIDI clocks (usually a quarter note but
	 * changeable with timesig).
	 */
	e = SEQ_MK_TIMING(TEMPO, .bpm=84);
	/*
	 * An ECHO event on output appears on input at the appointed time; the
	 * cookie can be anything of interest to the application. Can be used
	 * in schemes to get some control over latency.
	 */
	e = SEQ_MK_TIMING(ECHO, .cookie=0xfeedface);
	/*
	 * A midibeat is smaller than a beat. It is six MIDI clocks, or a fourth
	 * of a beat, or a sixteenth note if the beat is a quarter. SPP is a
	 * request to position at the requested midibeat from the start of the
	 * sequence. [sequencer does not at present implement SPP]
	 */
	e = SEQ_MK_TIMING(SPP, .midibeat=128);
	/*
	 * numerator and lg2denom describe the time signature as it would
	 * appear on a staff, where lg2denom of 0,1,2,3... corresponds to
	 * denominator of 1,2,4,8... respectively. So the example below
	 * corresponds to 4/4. dsq_per_24clks defines the relationship of
	 * MIDI clocks to note values, by specifying the number of
	 * demisemiquavers (32nd notes) represented by 24 MIDI clocks.
	 * The default is 8 demisemiquavers, or a quarter note.
	 * clks_per_click can configure a metronome (for example, the MPU401
	 * had such a feature in intelligent mode) to click every so many
	 * MIDI clocks. The 24 in this example would give a click every quarter
	 * note. [sequencer does not at present implement TIMESIG]
	 */
	e = SEQ_MK_TIMING(TIMESIG, .numerator=4, .lg2denom=2,
	                           .clks_per_click=24, .dsq_per_24clks=8);
	/*
	 * This example declares 6/8 time where the beat (24 clocks) is the
	 * eighth note, but the metronome clicks every dotted quarter (twice
	 * per measure):
	 */
	e = SEQ_MK_TIMING(TIMESIG, .numerator=6, .lg2denom=3,
	                           .clks_per_click=72, .dsq_per_24clks=4);
	/*
	 * An alternate declaration for 6/8 where the beat (24 clocks) is now
	 * the dotted quarter and corresponds to the metronome click:
	 */
	e = SEQ_MK_TIMING(TIMESIG, .numerator=6, .lg2denom=3,
	                           .clks_per_click=24, .dsq_per_24clks=12);
	/*
	 * It would also be possible to keep the default correspondence of
	 * 24 clocks to the quarter note (8 dsq), and still click the metronome
	 * each dotted quarter:
	 */
	e = SEQ_MK_TIMING(TIMESIG, .numerator=6, .lg2denom=3,
	                           .clks_per_click=36, .dsq_per_24clks=8);

	e = SEQ_MK_CHN(NOTEON,  .device=1, .channel=0, .key=60, .velocity=64);
	e = SEQ_MK_CHN(NOTEOFF, .device=1, .channel=0, .key=60, .velocity=64);
	e = SEQ_MK_CHN(KEY_PRESSURE, .device=1, .channel=0, .key=60,
	                             .pressure=64);
	
	/*
	 * sequencer does not at present implement CTL_CHANGE well. The API
	 * provides for a 14-bit value where you give the controller index
	 * of the controller MSB and sequencer will split the 14-bit value to
	 * the controller MSB and LSB for you--but it doesn't; it ignores the
	 * high bits of value and writes the low bits whether you have specified
	 * MSB or LSB. That would not be hard to fix but for the fact that OSS
	 * itself seems to suffer from the same mixup (and its behavior differs
	 * with whether the underlying device is an onboard synth or a MIDI
	 * link!) so there is surely a lot of code that relies on it being
	 * broken :(.
	 * (Note: as the OSS developers have ceased development of the
	 * /dev/music API as of OSS4, it would be possible given a complete
	 * list of the events defined in OSS4 to add some new ones for native
	 * use without fear of future conflict, such as a better ctl_change.)
	 */
	e = SEQ_MK_CHN(CTL_CHANGE, .device=1, .channel=0,
	               .controller=MIDI_CTRL_EXPRESSION_MSB, .value=8192);/*XX*/
	/*
	 * The way you really have to do it:
	 */
	e = SEQ_MK_CHN(CTL_CHANGE, .device=1, .channel=0,
	               .controller=MIDI_CTRL_EXPRESSION_MSB, .value=8192>>7);
	e = SEQ_MK_CHN(CTL_CHANGE, .device=1, .channel=0,
	               .controller=MIDI_CTRL_EXPRESSION_LSB, .value=8192&0x7f);

	e = SEQ_MK_CHN(PGM_CHANGE,   .device=1, .channel=0, .program=51);
	e = SEQ_MK_CHN(CHN_PRESSURE, .device=1, .channel=0, .pressure=64);
	e = SEQ_MK_CHN(PITCH_BEND,   .device=1, .channel=0, .value=8192);
	
	/*
	 * A SYSEX event carries up to six bytes of a system exclusive message.
	 * The first such message must begin with MIDI_SYSEX_START (0xf0), the
	 * last must end with MIDI_SYSEX_END (0xf7), and only the last may carry
	 * fewer than 6 bytes. To supply message bytes in the macro, you must
	 * prefix the first with [0]= as shown. The macro's first argument is
	 * the device.
	 */
	e = SEQ_MK_SYSEX(1,[0]=MIDI_SYSEX_START,1,2,MIDI_SYSEX_END);
	/*
	 * In some cases it may be easier to use the macro only to initialize
	 * the event, and fill in the message bytes later. The code that fills
	 * in the message does not need to store 0xff following the SYSEX_END.
	 */
	e = SEQ_MK_SYSEX(1);
	for ( i = 0; i < 3; ++ i )
		e.sysex.buffer[i] = i;
	/*
	 * It would be nice to think the old /dev/sequencer MIDIPUTC event
	 * obsolete, but it is still needed (absent any better API) by any MIDI
	 * file player that will implement the ESCAPED events that may occur in
	 * SMF. Sorry. Here's how to use it:
	 */
	e = SEQ_MK_EVENT(putc, SEQOLD_MIDIPUTC, .device=1, .byte=42);
	
	printf("confirm event size: %d (should be 8)\n", sizeof (seq_event_t));
	return 0;
}
#endif /* 0 */

#endif /* !_SYS_MIDIIO_H_ */