780 lines
25 KiB
C
780 lines
25 KiB
C
/* $NetBSD: midiio.h,v 1.15 2008/04/28 20:24:11 martin 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>
|
|
|
|
/*
|
|
* 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_ */
|