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