midisyn.c revision 1.9
1/* $NetBSD: midisyn.c,v 1.9 2001/11/13 05:32:50 lukem Exp $ */ 2 3/* 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (augustss@netbsd.org). 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39#include <sys/cdefs.h> 40__KERNEL_RCSID(0, "$NetBSD: midisyn.c,v 1.9 2001/11/13 05:32:50 lukem Exp $"); 41 42#include <sys/param.h> 43#include <sys/ioctl.h> 44#include <sys/fcntl.h> 45#include <sys/vnode.h> 46#include <sys/select.h> 47#include <sys/proc.h> 48#include <sys/malloc.h> 49#include <sys/systm.h> 50#include <sys/syslog.h> 51#include <sys/kernel.h> 52#include <sys/audioio.h> 53#include <sys/midiio.h> 54#include <sys/device.h> 55 56#include <dev/audio_if.h> 57#include <dev/midi_if.h> 58#include <dev/midivar.h> 59#include <dev/midisynvar.h> 60 61#ifdef AUDIO_DEBUG 62#define DPRINTF(x) if (midisyndebug) printf x 63#define DPRINTFN(n,x) if (midisyndebug >= (n)) printf x 64int midisyndebug = 0; 65#else 66#define DPRINTF(x) 67#define DPRINTFN(n,x) 68#endif 69 70int midisyn_findvoice(midisyn *, int, int); 71void midisyn_freevoice(midisyn *, int); 72int midisyn_allocvoice(midisyn *, u_int32_t, u_int32_t); 73u_int32_t midisyn_note_to_freq(int); 74u_int32_t midisyn_finetune(u_int32_t, int, int, int); 75 76int midisyn_open(void *, int, 77 void (*iintr)(void *, int), 78 void (*ointr)(void *), void *arg); 79void midisyn_close(void *); 80int midisyn_output(void *, int); 81void midisyn_getinfo(void *, struct midi_info *); 82int midisyn_ioctl(void *, u_long, caddr_t, int, struct proc *); 83 84struct midi_hw_if midisyn_hw_if = { 85 midisyn_open, 86 midisyn_close, 87 midisyn_output, 88 midisyn_getinfo, 89 midisyn_ioctl, 90}; 91 92static const int midi_lengths[] = { 3,3,3,3,2,2,3,1 }; 93/* Number of bytes in a MIDI command, including status */ 94#define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7]) 95 96int 97midisyn_open(void *addr, int flags, void (*iintr)(void *, int), 98 void (*ointr)(void *), void *arg) 99{ 100 midisyn *ms = addr; 101 102 DPRINTF(("midisyn_open: ms=%p ms->mets=%p\n", ms, ms->mets)); 103 if (ms->mets->open) 104 return (ms->mets->open(ms, flags)); 105 else 106 return (0); 107} 108 109void 110midisyn_close(void *addr) 111{ 112 midisyn *ms = addr; 113 struct midisyn_methods *fs; 114 int v; 115 116 DPRINTF(("midisyn_close: ms=%p ms->mets=%p\n", ms, ms->mets)); 117 fs = ms->mets; 118 for (v = 0; v < ms->nvoice; v++) 119 if (ms->voices[v].inuse) { 120 fs->noteoff(ms, v, 0, 0); 121 midisyn_freevoice(ms, v); 122 } 123 if (fs->close) 124 fs->close(ms); 125} 126 127void 128midisyn_getinfo(void *addr, struct midi_info *mi) 129{ 130 midisyn *ms = addr; 131 132 mi->name = ms->name; 133 mi->props = 0; 134} 135 136int 137midisyn_ioctl(void *maddr, u_long cmd, caddr_t addr, int flag, struct proc *p) 138{ 139 midisyn *ms = maddr; 140 141 if (ms->mets->ioctl) 142 return (ms->mets->ioctl(ms, cmd, addr, flag, p)); 143 else 144 return (EINVAL); 145} 146 147int 148midisyn_findvoice(midisyn *ms, int chan, int note) 149{ 150 u_int cn; 151 int v; 152 153 if (!(ms->flags & MS_DOALLOC)) 154 return (chan); 155 cn = MS_CHANNOTE(chan, note); 156 for (v = 0; v < ms->nvoice; v++) 157 if (ms->voices[v].chan_note == cn && ms->voices[v].inuse) 158 return (v); 159 return (-1); 160} 161 162void 163midisyn_attach(struct midi_softc *sc, midisyn *ms) 164{ 165 if (ms->flags & MS_DOALLOC) { 166 ms->voices = malloc(ms->nvoice * sizeof (struct voice), 167 M_DEVBUF, M_WAITOK); 168 memset(ms->voices, 0, ms->nvoice * sizeof (struct voice)); 169 ms->seqno = 1; 170 if (ms->mets->allocv == 0) 171 ms->mets->allocv = &midisyn_allocvoice; 172 } 173 sc->hw_if = &midisyn_hw_if; 174 sc->hw_hdl = ms; 175 DPRINTF(("midisyn_attach: ms=%p\n", sc->hw_hdl)); 176} 177 178void 179midisyn_freevoice(midisyn *ms, int voice) 180{ 181 if (!(ms->flags & MS_DOALLOC)) 182 return; 183 ms->voices[voice].inuse = 0; 184} 185 186int 187midisyn_allocvoice(midisyn *ms, u_int32_t chan, u_int32_t note) 188{ 189 int bestv, v; 190 u_int bestseq, s; 191 192 if (!(ms->flags & MS_DOALLOC)) 193 return (chan); 194 /* Find a free voice, or if no free voice is found the oldest. */ 195 bestv = 0; 196 bestseq = ms->voices[0].seqno + (ms->voices[0].inuse ? 0x40000000 : 0); 197 for (v = 1; v < ms->nvoice; v++) { 198 s = ms->voices[v].seqno; 199 if (ms->voices[v].inuse) 200 s += 0x40000000; 201 if (s < bestseq) { 202 bestseq = s; 203 bestv = v; 204 } 205 } 206 DPRINTFN(10,("midisyn_allocvoice: v=%d seq=%d cn=%x inuse=%d\n", 207 bestv, ms->voices[bestv].seqno, 208 ms->voices[bestv].chan_note, 209 ms->voices[bestv].inuse)); 210#ifdef AUDIO_DEBUG 211 if (ms->voices[bestv].inuse) 212 DPRINTFN(1,("midisyn_allocvoice: steal %x\n", 213 ms->voices[bestv].chan_note)); 214#endif 215 ms->voices[bestv].chan_note = MS_CHANNOTE(chan, note); 216 ms->voices[bestv].seqno = ms->seqno++; 217 ms->voices[bestv].inuse = 1; 218 return (bestv); 219} 220 221int 222midisyn_output(void *addr, int b) 223{ 224 midisyn *ms = addr; 225 u_int8_t status, chan; 226 int voice = 0; /* initialize to keep gcc quiet */ 227 struct midisyn_methods *fs; 228 u_int32_t note, vel; 229 230 DPRINTF(("midisyn_output: ms=%p b=0x%02x\n", ms, b)); 231 fs = ms->mets; 232 if (ms->pos < 0) { 233 /* Doing SYSEX */ 234 DPRINTF(("midisyn_output: sysex 0x%02x\n", b)); 235 if (fs->sysex) 236 fs->sysex(ms, b); 237 if (b == MIDI_SYSEX_END) 238 ms->pos = 0; 239 return (0); 240 } 241 if (ms->pos == 0 && !MIDI_IS_STATUS(b)) 242 ms->pos++; /* repeat last status byte */ 243 ms->buf[ms->pos++] = b; 244 status = ms->buf[0]; 245 if (ms->pos < MIDI_LENGTH(status)) 246 return (0); 247 /* Decode the MIDI command */ 248 chan = MIDI_GET_CHAN(status); 249 note = ms->buf[1]; 250 if (ms->flags & MS_FREQXLATE) 251 note = midisyn_note_to_freq(note); 252 vel = ms->buf[2]; 253 switch (MIDI_GET_STATUS(status)) { 254 case MIDI_NOTEOFF: 255 voice = midisyn_findvoice(ms, chan, ms->buf[1]); 256 if (voice >= 0) { 257 fs->noteoff(ms, voice, note, vel); 258 midisyn_freevoice(ms, voice); 259 } 260 break; 261 case MIDI_NOTEON: 262 voice = fs->allocv(ms, chan, ms->buf[1]); 263 fs->noteon(ms, voice, note, vel); 264 break; 265 case MIDI_KEY_PRESSURE: 266 if (fs->keypres) { 267 voice = midisyn_findvoice(ms, voice, ms->buf[1]); 268 if (voice >= 0) 269 fs->keypres(ms, voice, note, vel); 270 } 271 break; 272 case MIDI_CTL_CHANGE: 273 if (fs->ctlchg) 274 fs->ctlchg(ms, chan, ms->buf[1], vel); 275 break; 276 case MIDI_PGM_CHANGE: 277 if (fs->pgmchg) 278 fs->pgmchg(ms, chan, ms->buf[1]); 279 break; 280 case MIDI_CHN_PRESSURE: 281 if (fs->chnpres) { 282 voice = midisyn_findvoice(ms, chan, ms->buf[1]); 283 if (voice >= 0) 284 fs->chnpres(ms, voice, note); 285 } 286 break; 287 case MIDI_PITCH_BEND: 288 if (fs->pitchb) { 289 voice = midisyn_findvoice(ms, chan, ms->buf[1]); 290 if (voice >= 0) 291 fs->pitchb(ms, chan, note, vel); 292 } 293 break; 294 case MIDI_SYSTEM_PREFIX: 295 if (fs->sysex) 296 fs->sysex(ms, status); 297 ms->pos = -1; 298 return (0); 299 } 300 ms->pos = 0; 301 return (0); 302} 303 304/* 305 * Convert a MIDI note to the corresponding frequency. 306 * The frequency is scaled by 2^16. 307 */ 308u_int32_t 309midisyn_note_to_freq(int note) 310{ 311 int o, n, f; 312#define BASE_OCTAVE 5 313 static const u_int32_t notes[] = { 314 17145893, 18165441, 19245614, 20390018, 21602472, 22887021, 315 24247954, 25689813, 27217409, 28835840, 30550508, 32367136 316 }; 317 318 319 o = note / 12; 320 n = note % 12; 321 322 f = notes[n]; 323 324 if (o < BASE_OCTAVE) 325 f >>= (BASE_OCTAVE - o); 326 else if (o > BASE_OCTAVE) 327 f <<= (o - BASE_OCTAVE); 328 return (f); 329} 330 331u_int32_t 332midisyn_finetune(u_int32_t base_freq, int bend, int range, int vibrato_cents) 333{ 334 static const u_int16_t semitone_tuning[24] = 335 { 336/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, 337/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, 338/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 339 }; 340 static const u_int16_t cent_tuning[100] = 341 { 342/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, 343/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, 344/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, 345/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, 346/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, 347/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, 348/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, 349/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, 350/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, 351/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, 352/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, 353/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, 354/* 96 */ 10570, 10576, 10582, 10589 355 }; 356 u_int32_t amount; 357 int negative, semitones, cents, multiplier; 358 359 if (range == 0) 360 return base_freq; 361 362 if (base_freq == 0) 363 return base_freq; 364 365 if (range >= 8192) 366 range = 8192; 367 368 bend = bend * range / 8192; 369 bend += vibrato_cents; 370 371 if (bend == 0) 372 return base_freq; 373 374 if (bend < 0) { 375 bend = -bend; 376 negative = 1; 377 } else 378 negative = 0; 379 380 if (bend > range) 381 bend = range; 382 383 multiplier = 1; 384 while (bend > 2399) { 385 multiplier *= 4; 386 bend -= 2400; 387 } 388 389 semitones = bend / 100; 390 if (semitones > 99) 391 semitones = 99; 392 cents = bend % 100; 393 394 amount = semitone_tuning[semitones] * multiplier * cent_tuning[cents] 395 / 10000; 396 397 if (negative) 398 return (base_freq * 10000 / amount); /* Bend down */ 399 else 400 return (base_freq * amount / 10000); /* Bend up */ 401} 402 403