/* $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 #include /* * 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 #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 #include 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_ */